CMM 1 년 전
부모
커밋
714d726fe7
98개의 변경된 파일6711개의 추가작업 그리고 9개의 파일을 삭제
  1. +20
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/CountGroupByDTO.java
  2. +41
    -5
      pmapi/src/main/java/com/ningdatech/pmapi/common/util/BizUtils.java
  3. +29
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/common/util/StrUtils.java
  4. +20
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/expert/helper/PermissionCheckHelper.java
  5. +12
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertIntentionWorkRegionService.java
  6. +13
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertUserFullInfoService.java
  7. +17
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertIntentionWorkRegionServiceImpl.java
  8. +5
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertUserFullInfoServiceImpl.java
  9. +40
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/ExpertInviteBuilder.java
  10. +129
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/YxtSmsContextBuilder.java
  11. +77
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/DashboardController.java
  12. +173
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java
  13. +36
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/config/WebProperties.java
  14. +56
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteAvoidRule.java
  15. +54
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteIgnoreTime.java
  16. +55
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteRule.java
  17. +116
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/Meeting.java
  18. +73
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java
  19. +66
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpertEvaluation.java
  20. +39
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AbstractInviteRule.java
  21. +34
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AppointInviteRuleDTO.java
  22. +60
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidInfoDTO.java
  23. +31
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/CountConfirmByMeetingIdDTO.java
  24. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertChooseDTO.java
  25. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertDictChooseDTO.java
  26. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertTagChooseDTO.java
  27. +22
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingAndAttendStatusDTO.java
  28. +80
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java
  29. +46
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/RandomInviteRuleDTO.java
  30. +45
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatus.java
  31. +39
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertInviteType.java
  32. +36
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingDateTermType.java
  33. +73
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatus.java
  34. +30
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatusByDashboard.java
  35. +31
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/BatchAppointExpertsReq.java
  36. +49
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertEvaluationReq.java
  37. +43
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteCreateReq.java
  38. +36
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteIgnoreTimeModifyReq.java
  39. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertRemoveReq.java
  40. +82
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingBasicInfoModifyReq.java
  41. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCalenderReq.java
  42. +30
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCancelReq.java
  43. +21
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListByExpertReq.java
  44. +49
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java
  45. +34
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingResultReq.java
  46. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertAttendSummaryVO.java
  47. +48
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertBasicInfoVO.java
  48. +38
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertCountOnChangeVO.java
  49. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationListVO.java
  50. +38
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationToDoListItemVO.java
  51. +60
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInvitationDetailVO.java
  52. +95
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteDetailVO.java
  53. +43
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteIgnoreTimeListVO.java
  54. +30
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertListItemFoEvaluationVO.java
  55. +41
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertReplaceTodoListItemVO.java
  56. +72
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/InviteRuleDetailVO.java
  57. +76
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java
  58. +38
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCalenderItemVO.java
  59. +51
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingConfirmToDoListItemVO.java
  60. +46
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountByExpertVO.java
  61. +33
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountSummaryVO.java
  62. +97
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingDetailBasicVO.java
  63. +29
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingResultVO.java
  64. +31
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/RandomInviteRuleVO.java
  65. +80
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java
  66. +222
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java
  67. +50
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java
  68. +241
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/DashboardManage.java
  69. +672
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java
  70. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingExpertManage.java
  71. +708
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java
  72. +16
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.java
  73. +5
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.xml
  74. +16
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.java
  75. +5
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.xml
  76. +26
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.java
  77. +25
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.xml
  78. +81
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.java
  79. +82
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.xml
  80. +21
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.java
  81. +18
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.xml
  82. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java
  83. +24
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteRuleService.java
  84. +26
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertEvaluationService.java
  85. +123
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java
  86. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingService.java
  87. +45
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java
  88. +47
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteRuleServiceImpl.java
  89. +36
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertEvaluationServiceImpl.java
  90. +135
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertServiceImpl.java
  91. +42
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingServiceImpl.java
  92. +241
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java
  93. +29
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteExecutorConfig.java
  94. +326
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java
  95. +93
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/MeetingInviteCompleteNoticeTask.java
  96. +160
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/NoticeExpertAt24HoursBeforeMeetingTask.java
  97. +40
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/RandomInviteProperties.java
  98. +6
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/util/LoginUserUtil.java

+ 20
- 0
pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/CountGroupByDTO.java 파일 보기

@@ -0,0 +1,20 @@
package com.ningdatech.pmapi.common.model.entity;

import lombok.Data;

/**
* <p>
* CountGroupByDto
* </p>
*
* @author WendyYang
* @since 15:44 2022/8/28
*/
@Data
public class CountGroupByDTO<T> {

private Integer total;

private T groupKey;

}

+ 41
- 5
pmapi/src/main/java/com/ningdatech/pmapi/common/util/BizUtils.java 파일 보기

@@ -5,13 +5,13 @@ import com.ningdatech.basic.util.StrPool;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.NumberUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
* <p>
* BizUtils
@@ -26,6 +26,12 @@ public class BizUtils {

}

public static <T extends Collection<?>> void notEmpty(T objs, Consumer<T> consumer) {
if (objs != null && !objs.isEmpty()) {
consumer.accept(objs);
}
}

public static <T extends Number> List<T> splitToNum(String str, Class<T> aClass) {
if (StrUtil.isEmpty(str)) {
return Collections.emptyList();
@@ -46,7 +52,7 @@ public class BizUtils {
}
}

public static void notBlank(String str,Consumer<String> consumer) {
public static void notBlank(String str, Consumer<String> consumer) {
if (StrUtil.isNotBlank(str)) {
consumer.accept(str);
}
@@ -69,4 +75,34 @@ public class BizUtils {
return UUID.randomUUID().toString().replace("-", "");
}

/**
* 对象分组取第一条
*
* @param objs 对象集合
* @param group 分组函数
* @param comparator 比较器
* @return java.util.Collection<T>
* @author WendyYang
**/
public static <T> Collection<T> groupFirst(Collection<T> objs, Function<T, Object> group, Comparator<T> comparator) {
return groupFirstMap(objs, group, comparator).values();
}

/**
* 对象分组取第一条
*
* @param objs 对象集合
* @param group 分组函数
* @param comparator 比较器
* @return java.util.Collection<T>
* @author WendyYang
**/
public static <V, K> Map<K, V> groupFirstMap(Collection<V> objs, Function<V, K> group, Comparator<V> comparator) {
return objs.stream().collect(Collectors.groupingBy(group,
Collectors.collectingAndThen(toList(), w -> {
w.sort(comparator);
return w.get(0);
})));
}

}

+ 29
- 0
pmapi/src/main/java/com/ningdatech/pmapi/common/util/StrUtils.java 파일 보기

@@ -0,0 +1,29 @@
package com.ningdatech.pmapi.common.util;

import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;

import java.util.Collections;
import java.util.List;

/**
* <p>
* StrUtils
* </p>
*
* @author WendyYang
* @since 21:23 2023/2/23
*/
public class StrUtils extends StrUtil {

private StrUtils() {
}

public static List<String> split(String str) {
if (isBlank(str)) {
return Collections.emptyList();
}
return split(str, StrPool.COMMA);
}

}

+ 20
- 0
pmapi/src/main/java/com/ningdatech/pmapi/expert/helper/PermissionCheckHelper.java 파일 보기

@@ -0,0 +1,20 @@
package com.ningdatech.pmapi.expert.helper;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
* @author liuxinxin
* @date 2022/8/18 上午10:02
* 校验是否一个账号对一个专家有操作的权限
*/
@Component
@RequiredArgsConstructor
public class PermissionCheckHelper {

public boolean isSuperAdmin() {
// TODO
return false;
}

}

+ 12
- 0
pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertIntentionWorkRegionService.java 파일 보기

@@ -3,6 +3,8 @@ package com.ningdatech.pmapi.expert.service;
import com.ningdatech.pmapi.expert.entity.ExpertIntentionWorkRegion;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
* <p>
* 服务类
@@ -13,4 +15,14 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface IExpertIntentionWorkRegionService extends IService<ExpertIntentionWorkRegion> {

/**
* 根据履职意向地或许满足履职意向地的用户ID集合
*
* @param regionCode regionCode
* @param regionLevel regionLevel
* @return java.util.List<java.lang.Long>
* @author WendyYang
**/
List<Long> userIdsMatchIntentionRegion(String regionCode, Integer regionLevel);

}

+ 13
- 2
pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertUserFullInfoService.java 파일 보기

@@ -1,11 +1,13 @@
package com.ningdatech.pmapi.expert.service;

import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;

import java.util.List;

/**
* <p>
* 服务类
* 服务类
* </p>
*
* @author Liuxinxin
@@ -21,4 +23,13 @@ public interface IExpertUserFullInfoService extends IService<ExpertUserFullInfo>
* @return /
**/
ExpertUserFullInfo getByUserId(Long userId);

/**
* 查询用户信息
*
* @param userId 用户ID
* @return /
**/
List<ExpertUserFullInfo> listByUserId(List<Long> userId);

}

+ 17
- 2
pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertIntentionWorkRegionServiceImpl.java 파일 보기

@@ -1,14 +1,19 @@
package com.ningdatech.pmapi.expert.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.expert.entity.ExpertIntentionWorkRegion;
import com.ningdatech.pmapi.expert.mapper.ExpertIntentionWorkRegionMapper;
import com.ningdatech.pmapi.expert.service.IExpertIntentionWorkRegionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* <p>
* 服务实现类
* 服务实现类
* </p>
*
* @author Liuxinxin
@@ -17,4 +22,14 @@ import org.springframework.stereotype.Service;
@Service
public class ExpertIntentionWorkRegionServiceImpl extends ServiceImpl<ExpertIntentionWorkRegionMapper, ExpertIntentionWorkRegion> implements IExpertIntentionWorkRegionService {

@Override
public List<Long> userIdsMatchIntentionRegion(String regionCode, Integer regionLevel) {
LambdaQueryWrapper<ExpertIntentionWorkRegion> intentionQuery = Wrappers.lambdaQuery(ExpertIntentionWorkRegion.class)
.eq(ExpertIntentionWorkRegion::getRegionCode, regionCode)
.eq(ExpertIntentionWorkRegion::getRegionLevel, regionLevel)
.select(ExpertIntentionWorkRegion::getUserId);
List<ExpertIntentionWorkRegion> userIdMatchIntentionRegion = list(intentionQuery);
return CollUtils.fieldList(userIdMatchIntentionRegion, ExpertIntentionWorkRegion::getUserId);
}

}

+ 5
- 0
pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertUserFullInfoServiceImpl.java 파일 보기

@@ -28,4 +28,9 @@ public class ExpertUserFullInfoServiceImpl extends ServiceImpl<NdExpertUserFullI
return getOne(Wrappers.<ExpertUserFullInfo>lambdaQuery().eq(ExpertUserFullInfo::getUserId, userId));
}

@Override
public List<ExpertUserFullInfo> listByUserId(List<Long> userIds) {
return list(Wrappers.<ExpertUserFullInfo>lambdaQuery().in(ExpertUserFullInfo::getUserId, userIds));
}

}

+ 40
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/ExpertInviteBuilder.java 파일 보기

@@ -0,0 +1,40 @@
package com.ningdatech.pmapi.meeting.builder;

import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
import com.ningdatech.pmapi.user.entity.UserInfo;

/**
* <p>
* ExpertInviteBuilder
* </p>
*
* @author WendyYang
* @since 21:30 2022/11/21
*/
public class ExpertInviteBuilder {

private ExpertInviteBuilder() {
}

public static MeetingExpert getExpertByRandom(Long meetingId, ExpertUserFullInfo user, Long ruleId) {
return getExpertBasic(meetingId, user, ruleId, ExpertInviteType.RANDOM);
}

private static MeetingExpert getExpertBasic(Long meetingId, ExpertUserFullInfo user, Long ruleId, ExpertInviteType type) {
MeetingExpert expert = new MeetingExpert();
expert.setMeetingId(meetingId);
expert.setMobile(user.getPhoneNo());
expert.setExpertId(user.getId());
expert.setExpertName(user.getExpertName());
expert.setRuleId(ruleId);
expert.setInviteType(type.getCode());
return expert;
}

public static MeetingExpert getExpertByAppoint(Long meetingId, ExpertUserFullInfo user, Long ruleId) {
return getExpertBasic(meetingId, user, ruleId, ExpertInviteType.APPOINT);
}

}

+ 129
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/YxtSmsContextBuilder.java 파일 보기

@@ -0,0 +1,129 @@
//package com.ningdatech.pmapi.meeting.builder;
//
//
//import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
//import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
//import com.ningdatech.pmapi.sms.utils.DateUtil;
//
//import java.time.format.DateTimeFormatter;
//import java.util.ArrayList;
//import java.util.List;
//
///**
// * <p>
// * YxtSmsContextBuilder
// * </p>
// *
// * @author WendyYang
// * @since 14:19 2022/11/17
// */
//public class YxtSmsContextBuilder {
//
// private YxtSmsContextBuilder() {
// }
//
// public static List<SendSmsContext> smsToExpertByCancelMeeting(Meeting meeting, List<MeetingExpert> experts, String meetingType) {
// String holdCompany = meeting.getHoldCompanyBracket();
// String meetingTime = meeting.getStartTime().format(DateUtil.DTF_YMD_HM);
// List<SendSmsContext> contexts = new ArrayList<>();
// for (MeetingExpert me : experts) {
// SendSmsContext context = new SendSmsContext();
// context.setContent(holdCompany + String.format(YxtSmsTemplateConst.MEETING_CANCEL,
// me.getExpertName(), meeting.getCancelRemark(), meetingTime, meetingType, meeting.getContact()));
// context.setReceiveNumber(me.getMobile());
// contexts.add(context);
// }
// return contexts;
// }
//
// public static List<SendSmsContext> smsToExpertBySendNotice(Meeting meeting, List<MeetingExpert> experts, String meetingType) {
// String holdCompany = meeting.getHoldCompanyBracket();
// String meetingTime = meeting.getStartTime().format(DateUtil.DTF_YMD_HM);
// List<SendSmsContext> contexts = new ArrayList<>();
// for (MeetingExpert me : experts) {
// SendSmsContext context = new SendSmsContext();
// context.setContent(holdCompany + String.format(YxtSmsTemplateConst.SEND_MEETING_NOTICE,
// me.getExpertName(), me.getUpdateOn().format(DateUtil.DTF_YMD_HM),
// meetingType, meetingTime, meeting.getRegionDetail(), meeting.getContact()));
// context.setReceiveNumber(me.getMobile());
// contexts.add(context);
// }
// return contexts;
// }
//
// public static SendSmsContext smsToExpertByReplace(Meeting meeting, MeetingExpert expert, String meetingType) {
// SendSmsContext context = new SendSmsContext();
// String holdCompany = meeting.getHoldCompanyBracket();
// context.setContent(holdCompany + String.format(YxtSmsTemplateConst.EXPERT_REPLACED,
// expert.getExpertName(),
// meeting.getStartTime().format(DateUtil.DTF_YMD_HM),
// meetingType,
// meeting.getContact()
// ));
// context.setReceiveNumber(expert.getMobile());
// return context;
// }
//
// public static List<SendSmsContext> smsToExpertByMeetingChange(Meeting old, Meeting curr, List<MeetingExpert> experts, String meetingType) {
// List<SendSmsContext> contexts = new ArrayList<>();
// String holdCompany = curr.getHoldCompanyBracket();
// String sTimeOld = old.getStartTime().format(DateUtil.DTF_YMD_HM);
// String sTimeNew = curr.getStartTime().format(DateUtil.DTF_YMD_HM);
// for (MeetingExpert me : experts) {
// SendSmsContext context = new SendSmsContext();
// String content = String.format(YxtSmsTemplateConst.MEETING_INGO_CHANGE, me.getExpertName(),
// sTimeOld, meetingType, sTimeNew, curr.getRegionDetail(), curr.getContact());
// context.setContent(holdCompany + content);
// context.setReceiveNumber(me.getMobile());
// contexts.add(context);
// }
// return contexts;
// }
//
// public static SendSmsContext smsToInvitorByExpertLeave(UserBasicInfo invitor, String meetingName, String expertName) {
// SendSmsContext context = new SendSmsContext();
// context.setReceiveNumber(invitor.getPhoneNo());
// context.setContent(String.format(YxtSmsTemplateConst.TEMP_LEAVE_APPLY, invitor.getNickName(),
// meetingName, expertName, WebProperties.webUrl));
// return context;
// }
//
// public static SendSmsContext smsToExpertByLeavePassed(ExpertLeave leave, String leaveUser, String mobile) {
// String smsContent = String.format(YxtSmsTemplateConst.LEAVE_APPLY_PASSED, leaveUser,
// leave.getStartTime().format(DateTimeFormatter.ofPattern(DatePattern.YMD_HMS)),
// leave.getEndTime().format(DateTimeFormatter.ofPattern(DatePattern.YMD_HMS)));
// SendSmsContext context = new SendSmsContext();
// context.setContent(smsContent);
// context.setReceiveNumber(mobile);
// return context;
// }
//
// public static SendSmsContext smsToExpertBtLeaveReject(String leaveUser, String mobile, String opinion) {
// String smsContent = String.format(YxtSmsTemplateConst.LEAVE_APPLY_REFUSED, leaveUser, opinion);
// SendSmsContext context = new SendSmsContext();
// context.setContent(smsContent);
// context.setReceiveNumber(mobile);
// return context;
// }
//
// public static SendSmsContext smsByRandomInviteStop(String inviterName, String meetingName, String mobile) {
// String smsContent = String.format(YxtSmsTemplateConst.RANDOM_INVITE_STOP,
// inviterName, meetingName, WebProperties.webUrl
// );
// SendSmsContext context = new SendSmsContext();
// context.setContent(smsContent);
// context.setReceiveNumber(mobile);
// return context;
// }
//
//
// public static SendSmsContext meetingInviteCompleteNotice(Meeting meeting) {
// String smsContent = String.format(YxtSmsTemplateConst.RANDOM_EXTRACTION_COMPLETED,
// meeting.getConnecter(), meeting.getName(), WebProperties.webUrl
// );
// SendSmsContext context = new SendSmsContext();
// context.setContent(smsContent);
// context.setReceiveNumber(meeting.getContact());
// return context;
// }
//}

+ 77
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/DashboardController.java 파일 보기

@@ -0,0 +1,77 @@
package com.ningdatech.pmapi.meeting.controller;

import com.ningdatech.basic.model.PagePo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.log.annotation.WebLog;
import com.ningdatech.pmapi.meeting.entity.req.MeetingCalenderReq;
import com.ningdatech.pmapi.meeting.entity.vo.*;
import com.ningdatech.pmapi.meeting.manage.DashboardManage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.List;

/**
* <p>
* DashboardController
* </p>
*
* @author WendyYang
* @since 22:19 2022/8/24
*/
@Api(tags = "工作台相关接口")
@RestController
@AllArgsConstructor
@RequestMapping("/api/v1/dashboard")
public class DashboardController {

private final DashboardManage dashboardManage;

@ApiOperation("会议日历")
@GetMapping("/meetingCalender")
@WebLog(value = "会议日历")
public List<MeetingCalenderItemVO> meetingCalender(@Valid MeetingCalenderReq po) {
return dashboardManage.meetingCalender(po);
}

@ApiOperation("待办:专家评价列表")
@GetMapping("/todo/expertEvaluation")
@WebLog(value = "待办:专家评价列表")
public PageVo<ExpertEvaluationToDoListItemVO> expertEvaluationToDo(PagePo po) {
return dashboardManage.expertEvaluationToDo(po);
}

@ApiOperation("待办:专家待替换列表")
@GetMapping("/todo/expertReplace")
@WebLog(value = "待办:专家待替换列表")
public PageVo<ExpertReplaceTodoListItemVO> expertReplaceToDo(PagePo po) {
return dashboardManage.expertReplaceTodoList(po);
}

@ApiOperation("待办:专家待确认列表")
@GetMapping("/todo/expertConfirm")
@WebLog(value = "待办:专家待确认列表")
public PageVo<MeetingConfirmToDoListItemVO> expertConfirmToDo(PagePo po) {
return dashboardManage.expertConfirmToDo(po);
}

@ApiOperation("会议数量统计")
@GetMapping("/meetingCountSummary")
@WebLog(value = "会议数量统计")
public MeetingCountSummaryVO meetingCountSummary() {
return dashboardManage.meetingCountSummary();
}

@ApiOperation("专家工作台:会议数量统计")
@GetMapping("/meetingCountByExpert")
@WebLog(value = "专家工作台:会议数量统计")
public MeetingCountByExpertVO meetingCountByExpert() {
return dashboardManage.meetingCountByExpert();
}

}

+ 173
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java 파일 보기

@@ -0,0 +1,173 @@
package com.ningdatech.pmapi.meeting.controller;


import com.ningdatech.basic.model.IdVo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.log.annotation.WebLog;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingBasicDTO;
import com.ningdatech.pmapi.meeting.entity.req.*;
import com.ningdatech.pmapi.meeting.entity.vo.*;
import com.ningdatech.pmapi.meeting.manage.MeetingManage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
* <p>
* 会议 前端控制器
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Validated
@RestController
@AllArgsConstructor
@Api(tags = "会议管理")
@RequestMapping("/api/v1/meeting")
public class MeetingController {

private final MeetingManage meetingManage;

@PostMapping("/save")
@ApiOperation("新建会议")
@WebLog(value = "新建会议")
public IdVo<Long> meetingCreate(@Valid @RequestBody MeetingBasicDTO po) {
return meetingManage.save(po);
}

@PostMapping("/expertInviteByCreate")
@ApiOperation("新建会议-专家抽取")
@WebLog(value = "新建会议-专家抽取")
public void expertInviteByCreate(@Valid @RequestBody ExpertInviteCreateReq po) {
meetingManage.expertInviteByCreate(po);
}

@ApiOperation("专家抽取数量校验")
@PostMapping("/expertCountOnChange")
public ExpertCountOnChangeVO expertCountOnChange(@RequestBody ExpertInviteCreateReq po) {
return meetingManage.expertCountOnChange(po);
}

@ApiOperation("专家抽取员事务列表")
@GetMapping("/meetingListByManager")
@WebLog(value = "专家抽取员事务列表")
public PageVo<MeetingByManagerVO> meetingListByManager(MeetingListReq po) {
return meetingManage.meetingListByManager(po);
}

@ApiOperation("履职记录 | 专家会议列表")
@GetMapping("/meetingListByExpert")
@WebLog(value = "履职记录 | 专家会议列表")
public PageVo<MeetingByManagerVO> meetingListByExpert(MeetingListReq po) {
return meetingManage.meetingListByExpert(po);
}

@ApiOperation("会议详情-基本信息")
@GetMapping("detail/{meetingId}/basicInfo")
@WebLog(value = "会议详情-基本信息")
public MeetingDetailBasicVO meetingBasic(@PathVariable Long meetingId) {
return meetingManage.getMeetingBasicInfo(meetingId);
}

@ApiOperation("会议结果上传")
@PostMapping("/uploadMeetingResult")
@WebLog(value = "会议结果上传")
public void uploadMeetingResult(@Valid @RequestBody MeetingResultReq po) {
meetingManage.uploadMeetingResult(po);

}

@ApiOperation("会议结果详情")
@GetMapping("/detail/{meetingId}/meetingResult")
@WebLog(value = "会议结果详情况")
public MeetingResultVO meetingResultDetail(@PathVariable Long meetingId) {
return meetingManage.meetingResultDetail(meetingId);
}

@ApiOperation("邀请情况详情")
@GetMapping("/detail/{meetingId}/inviteDetail")
@WebLog(value = "邀请情况详请")
public ExpertInviteDetailVO inviteDetail(@PathVariable Long meetingId) {
return meetingManage.inviteDetail(meetingId);
}

@ApiOperation("会议基础信息修改")
@PostMapping("/basicInfo/modify")
@WebLog(value = "会议基础信息修改")
public void meetingBasicInfoModify(@Valid @RequestBody MeetingBasicInfoModifyReq po) {
meetingManage.meetingBasicInfoModify(po);
}

@ApiOperation("会议详情-抽取规则")
@GetMapping("/detail/inviteRule/{meetingId}")
@WebLog(value = "会议详情-抽取规则")
public InviteRuleDetailVO inviteRuleDetail(@PathVariable Long meetingId) {
return meetingManage.inviteRuleDetail(meetingId);
}

@ApiOperation("专家移除")
@PostMapping("/expertRemove")
@WebLog(value = "专家移除")
public void expertRemove(@RequestBody ExpertRemoveReq po) {
meetingManage.expertRemove(po);
}

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

}

@ApiOperation("确认名单(下发会议通知)")
@GetMapping("/sendMeetingNotice/{meetingId}")
@WebLog(value = "确认名单(下发会议通知")
public void sendMeetingNotice(@PathVariable Long meetingId) {
meetingManage.sendMeetingNotice(meetingId);

}

@ApiOperation("停止抽取")
@GetMapping("/stopInvite/{meetingId}")
@WebLog(value = "停止抽取")
public void stopInvite(@PathVariable Long meetingId) {
meetingManage.stopRandomInvite(meetingId);

}

@ApiOperation("取消会议")
@PostMapping("/cancelMeeting")
@WebLog(value = "取消会议")
public void cancelMeeting(@Valid @RequestBody MeetingCancelReq po) {
meetingManage.cancelMeeting(po);
}

@ApiOperation("邀请函信息")
@GetMapping("/expertInvitationDetail")
@WebLog(value = "邀请函信息")
public ExpertInvitationDetailVO expertInvitationDetail(@RequestParam("meetingId") Long meetingId,
@RequestParam(required = false) Long expertId) {
return meetingManage.expertInvitationDetail(meetingId, expertId);
}

@ApiOperation("批量补充专家")
@PostMapping("batchAppointExperts")
@WebLog(value = "批量补充专家")
public void batchAppointExperts(@Valid @RequestBody BatchAppointExpertsReq po) {
meetingManage.batchAppointExperts(po);
}

@ApiOperation("确认参加")
@PostMapping("/confirmAttendByManager")
@WebLog(value = "确认参加")
public void confirmAttendByManager(@RequestBody ExpertRemoveReq po) {
meetingManage.confirmAttendByManager(po);
}

}

+ 36
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/config/WebProperties.java 파일 보기

@@ -0,0 +1,36 @@
package com.ningdatech.pmapi.meeting.entity.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
* <p>
* SystemProperties
* </p>
*
* @author WendyYang
* @since 14:39 2022/9/15
*/
@Data
@Component
public class WebProperties {

public static String webUrl;

public static String provincialUrl;

@Value("${web.url:}")
private void setWebUrl(String url) {
webUrl = url;
}

/**
* 省局项目管理调用跳转地址
*/
@Value("${web.provincial:}")
private void setProvincial(String provincial) {
provincialUrl = provincial;
}

}

+ 56
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteAvoidRule.java 파일 보기

@@ -0,0 +1,56 @@
package com.ningdatech.pmapi.meeting.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* <p>
* ExpertInviteAvoidRule
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Data
@TableName("meeting_expert_invite_avoid_rule")
@ApiModel(value = "ExpertInviteAvoidRule对象")
public class ExpertInviteAvoidRule implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("事务ID")
private Long meetingId;

@ApiModelProperty("回避专家ID")
private String expertIds;

@ApiModelProperty("回避单位名称")
private String companyIds;

@ApiModelProperty("回避本单位同事")
private Boolean avoidMates;

@ApiModelProperty("创建人ID")
private Long createBy;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("修改时间")
private LocalDateTime updateOn;

@ApiModelProperty("修改人ID")
private Long updateBy;

}

+ 54
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteIgnoreTime.java 파일 보기

@@ -0,0 +1,54 @@
package com.ningdatech.pmapi.meeting.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;

/**
* <p>
* 专家抽取免打扰时间设置
* </p>
*
* @author WendyYang
* @since 2022-11-21
*/
@Data
@TableName("expert_invite_ignore_time")
@ApiModel(value = "ExpertInviteIgnoreTime对象", description = "专家抽取免打扰时间设置")
public class ExpertInviteIgnoreTime implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("是否启用")
private Boolean enable;

@ApiModelProperty("开始时间")
private LocalTime startTime;

@ApiModelProperty("截止时间")
private LocalTime endTime;

@ApiModelProperty("创建人ID")
private Long createBy;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("修改人ID")
private Long updateBy;

@ApiModelProperty("修改时间")
private LocalDateTime updateOn;

}

+ 55
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteRule.java 파일 보기

@@ -0,0 +1,55 @@
package com.ningdatech.pmapi.meeting.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* <p>
* ExpertInviteRule
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Data
@TableName("meeting_expert_invite_rule")
@ApiModel(value = "ExpertInviteRule对象")
public class ExpertInviteRule implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

private Long meetingId;

@ApiModelProperty("抽取规则")
private String inviteRule;

@ApiModelProperty("创建人ID")
private Long createBy;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("修改时间")
private LocalDateTime updateOn;

@ApiModelProperty("修改人ID")
private Long updateBy;

@ApiModelProperty("抽取类型:1 自定义规则、2 指定邀请")
private Integer inviteType;

@ApiModelProperty("抽取数量")
private Integer inviteCount;

}

+ 116
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/Meeting.java 파일 보기

@@ -0,0 +1,116 @@
package com.ningdatech.pmapi.meeting.entity.domain;

import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* <p>
* 会议-实体
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Data
@TableName("meeting")
@ApiModel(value = "Meeting对象", description = "会议")
public class Meeting implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("事务名称")
private String name;

@ApiModelProperty("事务类型")
private String type;

@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
private LocalDateTime endTime;

@ApiModelProperty("地区编码")
private String regionCode;

@ApiModelProperty("地区层级")
private Integer regionLevel;

@ApiModelProperty("地区详情")
private String regionDetail;

@ApiModelProperty("联系人")
private String connecter;

@ApiModelProperty("联系方式")
private String contact;

@ApiModelProperty("事务说明")
private String description;

@ApiModelProperty("附件")
private String attachment;

@ApiModelProperty("备注")
private String remark;

@ApiModelProperty("创建人ID")
@TableField(fill = FieldFill.INSERT)
private Long createBy;

@ApiModelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createOn;

@ApiModelProperty("修改人ID")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;

@ApiModelProperty("修改时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateOn;

@ApiModelProperty("创建人")
private String author;

@ApiModelProperty("创建人地区编码")
private String authorRegionCode;

@ApiModelProperty("创建人地区层级")
private Integer authorRegionLevel;

private Integer status;

private String resultDescription;

private String resultAttachment;

@ApiModelProperty("是否下发会议通知")
private Boolean sendMeetingNotice;

@ApiModelProperty("是否停止随机邀请")
private Boolean stopRandomInvite;

@ApiModelProperty("是否进行了专家抽取")
private Boolean invited;

@ApiModelProperty("取消说明")
private String cancelRemark;

@ApiModelProperty("举办单位")
private String holdCompany;

public String getHoldCompanyBracket() {
return "(" + this.getHoldCompany() + ")";
}

}

+ 73
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java 파일 보기

@@ -0,0 +1,73 @@
package com.ningdatech.pmapi.meeting.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
* <p>
* 事务专家表
* </p>
*
* @author WendyYang
* @since 2022-07-27
*/
@Data
@Builder
@TableName("meeting_expert")
@ApiModel(value = "MeetingExpert对象", description = "事务专家表")
public class MeetingExpert implements Serializable {

@Tolerate
public MeetingExpert() {
}

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("事务ID")
private Long meetingId;

@ApiModelProperty("专家ID")
private Long expertId;

@ApiModelProperty("邀请规则ID")
private Long ruleId;

private String mobile;

@ApiModelProperty("当前状态")
private Integer status;

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

private Long preId;

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

private Long createBy;

private LocalDateTime createOn;

private Long updateBy;

private LocalDateTime updateOn;

private String submitKey;

private String expertName;

}

+ 66
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpertEvaluation.java 파일 보기

@@ -0,0 +1,66 @@
package com.ningdatech.pmapi.meeting.entity.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ningdatech.pmapi.sys.model.entity.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
* <p>
* 专家评价表
* </p>
*
* @author WendyYang
* @since 2022-07-25
*/
@Data
@TableName("meeting_expert_evaluation")
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "MeetingExpertEvaluation对象", description = "专家评价表")
public class MeetingExpertEvaluation extends BaseEntity {

private static final long serialVersionUID = 1L;

@ApiModelProperty("主键")
@TableId(type = IdType.AUTO)
private Long id;

@ApiModelProperty("专家事务关联ID")
private Long expertMeetingId;

@ApiModelProperty("事务ID")
private Long meetingId;

@ApiModelProperty("专家ID")
private Long expertId;

@TableField(value = "is_attended")
@ApiModelProperty("是否参加")
private Boolean attended;

@TableField(value = "is_in_time")
@ApiModelProperty("是否准时参加")
private Boolean inTime;

@TableField(value = "is_violated")
@ApiModelProperty("是否违规")
private Boolean violated;

@ApiModelProperty("参与程度")
private Integer initiative;

@ApiModelProperty("建设性建议")
private String advice;

@ApiModelProperty("评审结果情况")
private String evaluateResult;

@ApiModelProperty("违规情况")
private String violation;

}

+ 39
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AbstractInviteRule.java 파일 보기

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

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

import javax.validation.constraints.NotNull;

/**
* <p>
* AbstractExpertExtractRule
* </p>
*
* @author WendyYang
* @since 10:10 2022/7/26
*/
@Data
@ApiModel("抽取规则")
public abstract class AbstractInviteRule {

@ApiModelProperty("id")
private Long id;

@ApiModelProperty("专家抽取数量")
@NotNull(message = "专家抽取数量不能为空", groups = {RuleSave.class})
private Integer count;

@ApiModelProperty("抽取类型:1 随机抽取、2 指定抽取")
@NotNull(message = "抽取类型不能为空", groups = {RuleSave.class, CountCheck.class})
private Integer inviteType;

public interface RuleSave {
}

public interface CountCheck {
}


}

+ 34
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AppointInviteRuleDTO.java 파일 보기

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

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

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

/**
* <p>
* AppointExtractRuleDto
* </p>
*
* @author WendyYang
* @since 10:19 2022/7/26
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel("指定抽取规则")
public class AppointInviteRuleDTO extends AbstractInviteRule {

@NotEmpty(message = "邀请说明不能为空", groups = {RuleSave.class})
@ApiModelProperty("邀请说明")
private String inviteDesc;

@NotEmpty(message = "专家ID不能为空", groups = {RuleSave.class, CountCheck.class})
@ApiModelProperty("专家ID")
private List<Long> expertIds;

}

+ 60
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidInfoDTO.java 파일 보기

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

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

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

/**
* <p>
* AvoidInfoDto
* </p>
*
* @author WendyYang
* @since 10:22 2022/7/26
*/
@Data
@ApiModel("回避信息")
public class AvoidInfoDTO {

@ApiModelProperty("回避单位")
@NotEmpty(message = "回避单位不能为空", groups = {AbstractInviteRule.RuleSave.class})
private List<String> companyIds;

@ApiModelProperty("是否回避同单位其他专家")
@NotNull(message = "是否回避同单位其他专家不能为空")
private Boolean avoidMates;

@ApiModelProperty("回避专家ID")
private List<Long> expertIds;

/**
* 返回一个默认值,仅在前端未传值时使用
*
* @return /
*/
public static AvoidInfoDTO defVal() {
AvoidInfoDTO val = new AvoidInfoDTO();
val.setAvoidMates(false);
val.setCompanyIds(Collections.emptyList());
val.setExpertIds(Collections.emptyList());
return val;
}

public void initDefVal() {
if (this.avoidMates == null) {
this.setAvoidMates(false);
}
if (this.companyIds == null) {
this.setCompanyIds(Collections.emptyList());
}
if (this.expertIds == null) {
this.setExpertIds(Collections.emptyList());
}
}

}

+ 31
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/CountConfirmByMeetingIdDTO.java 파일 보기

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

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

/**
* <p>
* CountConfirmByMeetingIdDto
* </p>
*
* @author WendyYang
* @since 14:21 2022/8/8
*/
@Data
@Builder
@AllArgsConstructor
public class CountConfirmByMeetingIdDTO {

@Tolerate
public CountConfirmByMeetingIdDTO() {
}

private Long meetingId;

private Integer total;

private Integer confirmed;

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertChooseDTO.java 파일 보기

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

import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.List;

/**
* <p>
* ExpertChooseDto
* </p>
*
* @author WendyYang
* @since 00:44 2022/7/27
*/
@Data
@AllArgsConstructor
public class ExpertChooseDTO {

private List<ExpertUserFullInfo> experts;

private Integer total;

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertDictChooseDTO.java 파일 보기

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

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

import java.util.List;

/**
* <p>
* ExpertDictChooseDTO
* </p>
*
* @author WendyYang
* @since 15:49 2022/8/9
*/
@Data
public class ExpertDictChooseDTO {

@ApiModelProperty("专家字典类型")
private String expertDict;

@ApiModelProperty("字典编码")
private List<String> dictCodes;

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertTagChooseDTO.java 파일 보기

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

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

import java.util.List;

/**
* <p>
* ExpertTagChooseDTO
* </p>
*
* @author WendyYang
* @since 19:45 2022/7/26
*/
@Data
public class ExpertTagChooseDTO {

@ApiModelProperty("专家标签")
private String expertTag;

@ApiModelProperty("标签编码")
private List<String> tagCodes;

}

+ 22
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingAndAttendStatusDTO.java 파일 보기

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

import lombok.Data;

/**
* <p>
* MeetingAndAttendStatusDto
* </p>
*
* @author WendyYang
* @since 22:48 2022/8/22
*/
@Data
public class MeetingAndAttendStatusDTO {

private Long meetingId;

private Integer status;

private Boolean attended;

}

+ 80
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java 파일 보기

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

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

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

/**
* <p>
* MeetingBasicDto
* </p>
*
* @author WendyYang
* @since 09:35 2022/7/26
*/
@Data
@ApiModel("会议基本信息")
public class MeetingBasicDTO {

@NotEmpty(message = "抽取单位不能为空")
@ApiModelProperty("抽取单位")
private String inviterCompany;

@NotEmpty(message = "事务名称不能为空")
@ApiModelProperty("事务名称")
private String name;

@NotEmpty(message = "事务类型不能为空")
@ApiModelProperty("事务类型")
private String type;

@NotNull(message = "开始时间不能为空")
@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@NotNull(message = "结束时间不能为空")
@ApiModelProperty("结束时间")
private LocalDateTime endTime;

@NotEmpty(message = "地区编码不能为空")
@ApiModelProperty("地区编码")
private String regionCode;

@NotNull(message = "地区编码层级不能为空")
@ApiModelProperty("地区编码层级")
private Integer regionLevel;

@NotEmpty(message = "地址详情不能为空")
@ApiModelProperty("地址详情")
private String regionDetail;

@NotEmpty(message = "联系人不能为空")
@ApiModelProperty("联系人")
private String connecter;

@NotEmpty(message = "联系方式不能为空")
@ApiModelProperty("联系方式")
private String contact;

@NotEmpty(message = "事务说明不能为空")
@ApiModelProperty("事务说明")
private String description;

@ApiModelProperty("附件ID")
private List<String> attachmentIds;

@ApiModelProperty("备注")
private String remark;

@ApiModelProperty("创建人")
private String author;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

}

+ 46
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/RandomInviteRuleDTO.java 파일 보기

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

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

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

/**
* <p>
* RandomExtractRuleDto
* </p>
*
* @author WendyYang
* @since 10:18 2022/7/26
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("随机抽取规则")
public class RandomInviteRuleDTO extends AbstractInviteRule {

@ApiModelProperty("专家字典信息")
private List<ExpertDictChooseDTO> expertDicts;

@ApiModelProperty("履职意向地编码")
private String intentionRegionCode;

@ApiModelProperty("履职意向地层级")
private Integer intentionRegionLevel;

@ApiModelProperty("专家层级编码")
private String expertRegionCode;

@ApiModelProperty("专家层级级别")
private Integer expertRegionLevel;

@ApiModelProperty("专家标签")
private List<ExpertTagChooseDTO> expertTags;

@ApiModelProperty("专家最长响应时间")
@NotNull(message = "专家最长响应时间不能为空", groups = {RuleSave.class})
private Integer waitForCallbackMinutes;

}

+ 45
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatus.java 파일 보기

@@ -0,0 +1,45 @@
package com.ningdatech.pmapi.meeting.entity.enumeration;

import lombok.Getter;

import java.util.Arrays;

/**
* <p>
* ExpertAttendStatus
* </p>
*
* @author WendyYang
* @since 09:23 2022/8/9
*/
@Getter
public enum ExpertAttendStatus {

NOTICING("通知中", 0),
NOT_ANSWERED("未应答", 1),
REPLACED("已替换", 2),
AGREED("同意参加", 3),
REFUSED("拒绝参加", 4),
CANCELED("已移除", 5),
ON_LEAVE("已请假", 6);

private final Integer code;
private final String desc;

ExpertAttendStatus(String desc, Integer code) {
this.code = code;
this.desc = desc;
}

public boolean eq(Integer code) {
return this.getCode().equals(code);
}

public static ExpertAttendStatus getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.eq(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("无效的邀请状态"));
}

}

+ 39
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertInviteType.java 파일 보기

@@ -0,0 +1,39 @@
package com.ningdatech.pmapi.meeting.entity.enumeration;

import lombok.Getter;

import java.util.Arrays;

/**
* <p>
* ExpertExtractRuleType
* </p>
*
* @author WendyYang
* @since 10:11 2022/7/26
*/
@Getter
public enum ExpertInviteType {

/**
* 专家抽取类型
*/
RANDOM(1, "随机邀请"),
APPOINT(2, "指定邀请");

private final Integer code;
private final String name;

ExpertInviteType(Integer code, String name) {
this.code = code;
this.name = name;
}

public static ExpertInviteType getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("无效的邀请类型"));
}

}

+ 36
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingDateTermType.java 파일 보기

@@ -0,0 +1,36 @@
package com.ningdatech.pmapi.meeting.entity.enumeration;

import lombok.Getter;

import java.util.Arrays;

/**
* <p>
* MeetingDateTermType-会议日期类型
* </p>
*
* @author WendyYang
* @since 09:54 2022/8/15
*/
@Getter
public enum MeetingDateTermType {

ONE_DAY(1, "一天"),
MORE_THAN_ONE(2, "两天及以上");

private final Integer code;
private final String name;

MeetingDateTermType(Integer code, String name) {
this.code = code;
this.name = name;
}

public static MeetingDateTermType getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("无效的会议日期类型编码"));
}

}

+ 73
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatus.java 파일 보기

@@ -0,0 +1,73 @@
package com.ningdatech.pmapi.meeting.entity.enumeration;

import lombok.Getter;

import java.util.Arrays;

/**
* <p>
* MeetingStatus
* </p>
*
* @author WendyYang
* @since 11:14 2022/8/8
*/
public class MeetingStatus {

/**
* 管理员事务列表:事务状态
*/
@Getter
public enum Manager {

UNCOMPLETED("未完成", 1),
COMPLETED("已完成", 2),
CANCELED("已取消", 3);

private final String desc;
private final Integer code;

Manager(String desc, Integer code) {
this.desc = desc;
this.code = code;
}

public boolean eq(Integer code) {
return this.getCode().equals(code);
}

public static Manager getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("状态编码"));
}

}

@Getter
public enum Expert {

TO_ATTEND("待参加", 1),
ATTENDED("已参加", 2),
ON_LEAVE("已请假", 3),
UN_ATTEND("缺席", 4);

private final String desc;
private final Integer code;

Expert(String desc, Integer code) {
this.desc = desc;
this.code = code;
}

public static Expert getByCode(Integer code) {
return Arrays.stream(values())
.filter(w -> w.getCode().equals(code))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("状态编码"));
}

}

}

+ 30
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatusByDashboard.java 파일 보기

@@ -0,0 +1,30 @@
package com.ningdatech.pmapi.meeting.entity.enumeration;

/**
* <p>
* MeetingStatusByDashboard
* </p>
*
* @author WendyYang
* @since 14:02 2022/8/30
*/
public enum MeetingStatusByDashboard {

/**
* 已完成
*/
COMPLETED,
/**
* 已取消
*/
CANCELED,
/**
* 待召开
*/
TO_BE_HELD,
/**
* 已召开
*/
HELD

}

+ 31
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/BatchAppointExpertsReq.java 파일 보기

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

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

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

/**
* <p>
* BatchAppointExpertsPo
* </p>
*
* @author WendyYang
* @since 15:49 2022/8/26
*/
@Data
@ApiModel("补充专家参数实体")
public class BatchAppointExpertsReq {

@NotEmpty(message = "专家ID不能为空")
@ApiModelProperty("专家ID")
private List<Long> expertIds;

@NotNull(message = "会议ID不能为空")
@ApiModelProperty("会议ID")
private Long meetingId;

}

+ 49
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertEvaluationReq.java 파일 보기

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

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

import javax.validation.constraints.NotNull;

/**
* <p>
* ExpertEvaluationPo
* </p>
*
* @author WendyYang
* @since 15:30 2022/7/25
*/
@Data
@ApiModel("专家评价实体")
public class ExpertEvaluationReq {

@ApiModelProperty("专家事务关联ID")
@NotNull(message = "专家事务关联ID不能为空")
private Long expertMeetingId;

@ApiModelProperty("是否参加")
@NotNull(message = "是否参加不能为空")
private Boolean attended;

@ApiModelProperty("是否准时参加")
private Boolean inTime;

@ApiModelProperty("是否违规")
@NotNull(message = "是否违规不能为空")
private Boolean violated;

@ApiModelProperty("违规情况")
private String violation;

@ApiModelProperty("参与程度:1 很积极...5 很消极")
@NotNull(message = "参与程度不能为空")
private Integer initiative;

@ApiModelProperty("建设性的建议")
private String advice;

@ApiModelProperty("评审结果")
private String evaluateResult;

}

+ 43
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteCreateReq.java 파일 보기

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

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.RandomInviteRuleDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

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

/**
* <p>
* MeetingCreatePo
* </p>
*
* @author WendyYang
* @since 10:32 2022/7/26
*/
@Data
@ApiModel("专家抽取新家实体")
public class ExpertInviteCreateReq {

@NotNull(message = "会议ID不能为空", groups = {AbstractInviteRule.RuleSave.class, AbstractInviteRule.CountCheck.class})
@ApiModelProperty("会议ID")
private Long meetingId;

@Valid
@ApiModelProperty("回避信息")
private AvoidInfoDTO avoidInfo;

@Valid
@ApiModelProperty("随机抽取规则")
private List<RandomInviteRuleDTO> randomRules;

@Valid
@ApiModelProperty("指定抽取规则")
private AppointInviteRuleDTO appointRule;

}

+ 36
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteIgnoreTimeModifyReq.java 파일 보기

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

import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.time.LocalTime;

/**
* <p>
* 专家抽取免打扰时间设置
* </p>
*
* @author WendyYang
* @since 2022-11-21
*/
@Data
@ApiModel("专家抽取免打扰时间设置")
public class ExpertInviteIgnoreTimeModifyReq {

@ApiModelProperty("主键")
private Long id;

@ApiModelProperty("是否启用:true 是、false 否")
private Boolean enable;

@ApiModelProperty("开始时间(格式:HH:mm)")
@JSONField(format = "HH:mm")
private LocalTime startTime;

@ApiModelProperty("截止时间(格式:HH:mm)")
@JSONField(format = "HH:mm")
private LocalTime endTime;

}

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertRemoveReq.java 파일 보기

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

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

/**
* <p>
* ExpertRemovePo
* </p>
*
* @author WendyYang
* @since 08:59 2022/8/10
*/
@Data
@ApiModel("专家(移除/替换)实体")
public class ExpertRemoveReq {

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

@ApiModelProperty("专家ID")
private Long expertId;

@ApiModelProperty("专家会议ID")
private Long expertMeetingId;

}

+ 82
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingBasicInfoModifyReq.java 파일 보기

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

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

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

/**
* <p>
* MeetingBasicInfoModifyPo
* </p>
*
* @author WendyYang
* @since 11:15 2022/8/17
*/
@Data
public class MeetingBasicInfoModifyReq {

@NotEmpty(message = "抽取单位不能为空")
@ApiModelProperty("抽取单位")
private String inviterCompany;

@ApiModelProperty("会议ID")
@NotNull(message = "会议ID不能为空")
private Long id;

@NotEmpty(message = "会议名称不能为空")
@ApiModelProperty("会议名称")
private String name;

@NotEmpty(message = "会议类型不能为空")
@ApiModelProperty("会议类型")
private String type;

@NotNull(message = "开始时间不能为空")
@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@NotNull(message = "结束时间不能为空")
@ApiModelProperty("结束时间")
private LocalDateTime endTime;

@NotEmpty(message = "地区编码不能为空")
@ApiModelProperty("地区编码")
private String regionCode;

@NotNull(message = "地区编码层级不能为空")
@ApiModelProperty("地区编码层级")
private Integer regionLevel;

@NotEmpty(message = "地址详情不能为空")
@ApiModelProperty("地址详情")
private String regionDetail;

@NotEmpty(message = "联系人不能为空")
@ApiModelProperty("联系人")
private String connecter;

@NotEmpty(message = "联系方式不能为空")
@ApiModelProperty("联系方式")
private String contact;

@NotEmpty(message = "会议说明不能为空")
@ApiModelProperty("会议说明")
private String description;

@ApiModelProperty("附件ID")
private List<String> attachmentIds;

@ApiModelProperty("备注")
private String remark;

@ApiModelProperty("创建人")
private String author;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

}

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCalenderReq.java 파일 보기

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

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.NotNull;
import java.time.LocalDate;

/**
* <p>
* MeetingCalenderPo
* </p>
*
* @author WendyYang
* @since 22:38 2022/8/24
*/
@Data
public class MeetingCalenderReq {

@NotNull(message = "开始日期不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;

@NotNull(message = "结束日期不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;

}

+ 30
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCancelReq.java 파일 보기

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

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

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
* <p>
* MeetingCancelPo
* </p>
*
* @author WendyYang
* @since 10:43 2022/8/26
*/
@Data
@ApiModel("会议取消实体")
public class MeetingCancelReq {

@NotNull(message = "会议ID不能为空")
@ApiModelProperty("会议ID")
private Long meetingId;

@NotBlank(message = "取消说明不能为空")
@ApiModelProperty("取消说明")
private String cancelRemark;

}

+ 21
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListByExpertReq.java 파일 보기

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

import com.ningdatech.basic.model.PagePo;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
* <p>
* MeetingListByExpertPo
* </p>
*
* @author WendyYang
* @since 17:02 2022/8/23
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class MeetingListByExpertReq extends PagePo {

private Long expertId;

}

+ 49
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java 파일 보기

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

import com.ningdatech.basic.model.PagePo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;

/**
* <p>
* MeetingListByManagerPo
* </p>
*
* @author WendyYang
* @since 11:07 2022/8/8
*/
@Data
@ApiModel("专家抽取员事务列表请求参数")
@EqualsAndHashCode(callSuper = true)
public class MeetingListReq extends PagePo {

@ApiModelProperty("事务名称")
private String name;

@ApiModelProperty("事务开始时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;

@ApiModelProperty("事务结束时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime;

@ApiModelProperty("地点")
private String address;

@ApiModelProperty("事务状态:1 未完成、2 已完成、3 已取消\n" +
"专家参与状态:1 待参加、2 已参加、3 已请假")
private Integer status;

@ApiModelProperty("事务类型")
private String type;

@ApiModelProperty("专家ID")
private Long expertId;

}

+ 34
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingResultReq.java 파일 보기

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

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

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

/**
* <p>
* MeetingResultPo
* </p>
*
* @author WendyYang
* @since 21:20 2022/8/8
*/
@Data
@ApiModel("会议结果上传实体")
public class MeetingResultReq {

@NotNull(message = "事务ID不能为空")
@ApiModelProperty("事务ID")
private Long meetingId;

@NotBlank(message = "会议结果说明不能为空")
@ApiModelProperty("会议结果说明")
private String resultDescription;

@ApiModelProperty("附件ID")
private List<Long> attachments;

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertAttendSummaryVO.java 파일 보기

@@ -0,0 +1,25 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

/**
* <p>
* ExpertAttendSummaryVo
* </p>
*
* @author WendyYang
* @since 11:18 2022/8/28
*/
@Data
public class ExpertAttendSummaryVO {

private transient Long expertId;

@ApiModelProperty("专家姓名")
private String expertName;

@ApiModelProperty("专家出席次数")
private Integer attendCount;

}

+ 48
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertBasicInfoVO.java 파일 보기

@@ -0,0 +1,48 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.ningdatech.pmapi.sys.model.dto.RegionDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

import java.util.List;

/**
* <p>
* ExpertBasicInfoVo
* </p>
*
* @author WendyYang
* @since 15:58 2022/8/8
*/
@Data
@Builder
public class ExpertBasicInfoVO {

@Tolerate
public ExpertBasicInfoVO() {
}

@ApiModelProperty("专家ID")
private Long expertId;

@ApiModelProperty("专家姓名")
private String name;

@ApiModelProperty("专家层级")
private List<RegionDTO> regions;

@ApiModelProperty("职称级别")
private String jobLevel;

@ApiModelProperty("单位属性")
private String companyType;

@ApiModelProperty("工作单位")
private String company;

@ApiModelProperty("联系方式")
private String contact;

}

+ 38
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertCountOnChangeVO.java 파일 보기

@@ -0,0 +1,38 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* <p>
* ExpertCountOnChangeVo
* </p>
*
* @author WendyYang
* @since 10:02 2022/8/8
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("专家抽取数量校验实体")
public class ExpertCountOnChangeVO {

@ApiModelProperty("每个随机规则对应的可抽取数量")
private List<Integer> countList;

@ApiModelProperty("参数是否通过校验:true 是、false 否")
private Boolean status;

@ApiModelProperty("错误提醒")
private String message;

public void addCountList(Integer count) {
this.countList.add(count);
}

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationListVO.java 파일 보기

@@ -0,0 +1,25 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

/**
* <p>
* ExpertEvaluationListVo
* </p>
*
* @author WendyYang
* @since 16:03 2022/8/8
*/
@Data
@ApiModel("专家评价列表")
public class ExpertEvaluationListVO {

@ApiModelProperty("专家评价ID")
private Long evaluationId;

@ApiModelProperty("专家信息")
private ExpertBasicInfoVO expert;

}

+ 38
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationToDoListItemVO.java 파일 보기

@@ -0,0 +1,38 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

import java.time.LocalDateTime;

/**
* <p>
* ExpertEvaluationToDoListItemVo
* </p>
*
* @author WendyYang
* @since 13:58 2022/8/29
*/
@Data
@ApiModel("专家评价待办列表")
@EqualsAndHashCode(callSuper = true)
public class ExpertEvaluationToDoListItemVO extends ExpertBasicInfoVO {

@ApiModelProperty("专家会议ID")
private Long expertMeetingId;

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

@ApiModelProperty("会议名称")
private String meetingName;

@ApiModelProperty("会议开始时间")
private LocalDateTime startTime;

@ApiModelProperty("会议结束时间")
private LocalDateTime endTime;

}

+ 60
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInvitationDetailVO.java 파일 보기

@@ -0,0 +1,60 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.alibaba.fastjson.annotation.JSONField;
import com.ningdatech.pmapi.sys.model.dto.RegionDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* ExpertInvitationDetailVo
* </p>
*
* @author WendyYang
* @since 13:51 2022/8/27
*/
@Data
@Builder
public class ExpertInvitationDetailVO {

@Tolerate
public ExpertInvitationDetailVO() {
}

@ApiModelProperty("专家确认时间")
private LocalDateTime inviteTime;

@ApiModelProperty("会议举办单位")
private String holdCompany;

@ApiModelProperty("会议开始时间")
@JSONField(format = "yyyy-MM-dd HH:mm")
private LocalDateTime startTime;

@ApiModelProperty("会议结束时间")
@JSONField(format = "yyyy-MM-dd HH:mm")
private LocalDateTime endTime;

@ApiModelProperty("会议名称")
private String meetingName;

@ApiModelProperty("举办地址详情")
private String regionDetail;

private List<RegionDTO> regions;

@ApiModelProperty("联系方式")
private String contact;

@ApiModelProperty("联系人")
private String connecter;

@ApiModelProperty("专家名称")
private String expertName;

}

+ 95
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteDetailVO.java 파일 보기

@@ -0,0 +1,95 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

import java.util.ArrayList;
import java.util.List;

/**
* <p>
* ExpertInviteDetailVo
* </p>
*
* @author WendyYang
* @since 08:58 2022/8/9
*/
@Data
@ApiModel("专家邀请情况实体")
public class ExpertInviteDetailVO {

@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("最终参与名单实体")
public static class ExpertAttendListItemVO extends ExpertBasicInfoVO {

private Long meetingId;

private Long expertMeetingId;

@ApiModelProperty("邀请方式")
private String inviteType;

}

@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("随机邀请名单实体")
public static class RandomInviteListItemVO extends ExpertBasicInfoVO {

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

@ApiModelProperty("专家会议ID")
private Long expertMeetingId;

@ApiModelProperty("电话通知状态")
private String noticeStatus;

@ApiModelProperty("邀请结果")
private String confirmResult;

@ApiModelProperty("邀请状态")
private Integer status;

}

@ApiModelProperty("参与数量总计")
private Integer attendTotal = 0;

@ApiModelProperty("随机邀请参与数量")
private Integer randomAttend = 0;

@ApiModelProperty("指定邀请参与数量")
private Integer appointAttend = 0;

@ApiModelProperty("是否已停止邀请")
private Boolean hasStopInvite;

@ApiModelProperty("是否已下发通知")
private Boolean hasSendNotice;

@ApiModelProperty("最终参与名单")
private List<ExpertAttendListItemVO> attendList = new ArrayList<>();

@ApiModelProperty("随机邀请名单")
private List<RandomInviteListItemVO> randomInviteList = new ArrayList<>();

@ApiModelProperty("指定邀请名单")
private List<RandomInviteListItemVO> appointInviteList = new ArrayList<>();

public void addAttendList(ExpertAttendListItemVO attend) {
this.attendList.add(attend);
}

public void addRandomInviteList(RandomInviteListItemVO randomInvite) {
this.randomInviteList.add(randomInvite);
}

public void addAppointInviteList(RandomInviteListItemVO appointInvite) {
this.appointInviteList.add(appointInvite);
}

}

+ 43
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteIgnoreTimeListVO.java 파일 보기

@@ -0,0 +1,43 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.time.LocalDateTime;
import java.time.LocalTime;

/**
* <p>
* 专家抽取免打扰时间设置
* </p>
*
* @author WendyYang
* @since 2022-11-21
*/
@Data
@ApiModel("专家抽取免打扰时间设置")
public class ExpertInviteIgnoreTimeListVO {

@ApiModelProperty("主键")
private Long id;

@ApiModelProperty("是否启用:true 是、false 否")
private Boolean enable;

@ApiModelProperty("开始时间")
@JSONField(format = "HH:mm")
private LocalTime startTime;

@ApiModelProperty("截止时间")
@JSONField(format = "HH:mm")
private LocalTime endTime;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("修改时间")
private LocalDateTime updateOn;

}

+ 30
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertListItemFoEvaluationVO.java 파일 보기

@@ -0,0 +1,30 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

/**
* <p>
* MeetingExpertListItemFoEvaluationVo
* </p>
*
* @author WendyYang
* @since 10:06 2022/8/24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("专家评价专家列表视图实体")
public class ExpertListItemFoEvaluationVO extends ExpertBasicInfoVO {

@ApiModelProperty("是否已评价:true 已评价、false 未评价")
private boolean evaluated;

@ApiModelProperty("评价ID")
private Long evaluationId;

@ApiModelProperty("专家会议ID")
private Long expertMeetingId;

}

+ 41
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertReplaceTodoListItemVO.java 파일 보기

@@ -0,0 +1,41 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

import java.time.LocalDateTime;

/**
* <p>
* ExpertReplaceTodoListItemVo
* </p>
*
* @author WendyYang
* @since 16:46 2022/8/29
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ExpertReplaceTodoListItemVO extends ExpertBasicInfoVO {

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

private Long expertMeetingId;

@ApiModelProperty("会议名称")
private String meetingName;

@ApiModelProperty("邀请结果")
private Integer status;

@ApiModelProperty("邀请方式")
private String inviteType;

@ApiModelProperty("开始时间")
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
private LocalDateTime endTime;

}

+ 72
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/InviteRuleDetailVO.java 파일 보기

@@ -0,0 +1,72 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

import java.util.List;

/**
* <p>
* InviteRuleDetailVo
* </p>
*
* @author WendyYang
* @since 14:58 2022/8/17
*/
@Data
public class InviteRuleDetailVO {

@ApiModelProperty("是否创建邀请规则")
private Boolean invited;

@ApiModelProperty("随机邀请规则")
private List<RandomInviteRuleVO> randomRules;

@ApiModelProperty("是否有指定抽取规则")
private Boolean hasAppointRule;

@ApiModelProperty("指定抽取规则")
private AppointRuleVo appointRule;

@ApiModelProperty("是否有回避规则")
private Boolean hasAvoidInfo;

@ApiModelProperty("回避信息")
private AvoidInfoVo avoidInfo;

@Data
public static class AvoidInfoVo {

private List<String> companyIds;

private Boolean avoidMates;

private List<ExpertBasicInfoVO> experts;

}

@Data
public static class AppointRuleVo {

private String inviteDesc;

private List<ExpertBasicInfoVO> experts;

}

public Boolean setAndGetHasAppointRule(Boolean hasAppointRule) {
this.hasAppointRule = hasAppointRule;
return this.hasAppointRule;
}

public Boolean setAndGetHasAvoidInfo(Boolean hasAvoidInfo) {
this.hasAvoidInfo = hasAvoidInfo;
return this.hasAvoidInfo;
}

public Boolean setAndGetInvited(Boolean invited) {
this.invited = invited;
return this.invited;
}

}

+ 76
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java 파일 보기

@@ -0,0 +1,76 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

import java.time.LocalDateTime;

/**
* <p>
* MeetingByManagerVo
* </p>
*
* @author WendyYang
* @since 11:43 2022/8/8
*/
@Data
@Builder
@ApiModel("会议实体(管理员列表)")
public class MeetingByManagerVO {

@Tolerate
public MeetingByManagerVO() {
}

@ApiModelProperty("事务ID")
private Long id;

@ApiModelProperty("事务状态")
private Integer status;

@ApiModelProperty("会议参加状态:1 待参加、2 已参加、3 已请假")
private Integer attendStatus;

@ApiModelProperty("事务名称")
private String name;

@ApiModelProperty("联系人")
private String connecter;

@ApiModelProperty("联系方式")
private String contact;

@ApiModelProperty("会议地址详情")
private String meetingAddress;

@ApiModelProperty("事务类型编码")
private String dateType;

@ApiModelProperty("事务类型名称")
private String dateTypeName;

@ApiModelProperty("事务开始时间")
@JSONField(format = "yyyy-MM-dd HH:mm")
private LocalDateTime startTime;

@ApiModelProperty("事务结束时间")
@JSONField(format = "yyyy-MM-dd HH:mm")
private LocalDateTime endTime;

@ApiModelProperty("邀请人数")
private Integer inviteCount;

@ApiModelProperty("确认人数")
private Integer confirmCount;

@ApiModelProperty("是否进行了专家抽取:true 是、false 否")
private Boolean invited;

@ApiModelProperty("抽取单位")
private String inviterCompany;

}

+ 38
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCalenderItemVO.java 파일 보기

@@ -0,0 +1,38 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

import java.time.LocalDate;
import java.util.Collection;
import java.util.List;

/**
* <p>
* MeetingCalenderItemVo
* </p>
*
* @author WendyYang
* @since 22:57 2022/8/24
*/
@Data
@ApiModel("会议日历视图实体")
public class MeetingCalenderItemVO {

@ApiModelProperty("当前日期")
private LocalDate today;

@ApiModelProperty("是否请假")
private Boolean hasLeave;

@ApiModelProperty("请假ID")
private Collection<Long> leaveIds;

@ApiModelProperty("是否有会议")
private Boolean hasMeeting;

@ApiModelProperty("会议信息")
private List<MeetingByManagerVO> meetings;

}

+ 51
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingConfirmToDoListItemVO.java 파일 보기

@@ -0,0 +1,51 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.ningdatech.pmapi.sys.model.dto.RegionDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* MeetingConfirmToDoListItem
* </p>
*
* @author WendyYang
* @since 15:20 2022/8/29
*/
@Data
public class MeetingConfirmToDoListItemVO {

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

@ApiModelProperty("会议名称")
private String meetingName;

@ApiModelProperty("会议开始时间")
private LocalDateTime startTime;

@ApiModelProperty("会议结束时间")
private LocalDateTime endTime;

@ApiModelProperty("会议类型")
private String type;

@ApiModelProperty("举办地址详情")
private String regionDetail;

@ApiModelProperty("举办地址")
private List<RegionDTO> regions;

@ApiModelProperty("状态")
private Integer status;

@ApiModelProperty("邀请总人数")
private Integer totalExpert;

@ApiModelProperty("确认总人数")
private Integer confirmedExpert;

}

+ 46
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountByExpertVO.java 파일 보기

@@ -0,0 +1,46 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

/**
* <p>
* MeetingCountByExpertVo
* </p>
*
* @author WendyYang
* @since 10:23 2022/10/19
*/
@Data
@AllArgsConstructor
@ApiModel("专家工作台:会议数量统计视图")
public class MeetingCountByExpertVO {

@ApiModelProperty("待参加数量")
private Integer toBeAttended;

@ApiModelProperty("已参加数量")
private Integer attended;

@ApiModelProperty("请假数量")
private Integer leaved;

public static MeetingCountByExpertVO init() {
return new MeetingCountByExpertVO(0, 0, 0);
}

public void incrToBeAttended() {
this.toBeAttended += 1;
}

public void incrAttended() {
this.attended += 1;
}

public void incrLeaved() {
this.leaved += 1;
}

}

+ 33
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountSummaryVO.java 파일 보기

@@ -0,0 +1,33 @@
package com.ningdatech.pmapi.meeting.entity.vo;

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

/**
* <p>
* MeetingCountSummaryVo
* </p>
*
* @author WendyYang
* @since 09:43 2022/8/30
*/
@Data
@Builder
@ApiModel("会议数量统计视图实体")
public class MeetingCountSummaryVO {

@ApiModelProperty("已完成")
private Integer completed;

@ApiModelProperty("已取消")
private Integer canceled;

@ApiModelProperty("待召开")
private Integer toBeHeld;

@ApiModelProperty("已召开")
private Integer held;

}

+ 97
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingDetailBasicVO.java 파일 보기

@@ -0,0 +1,97 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.alibaba.fastjson.annotation.JSONField;
import com.ningdatech.file.entity.vo.result.AttachFileVo;
import com.ningdatech.pmapi.sys.model.dto.RegionDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;

import java.time.LocalDateTime;
import java.util.List;

/**
* <p>
* MeetingDetailBasicVo
* </p>
*
* @author WendyYang
* @since 15:16 2022/8/8
*/
@Data
@Builder
@ApiModel("会议详情基本信息")
public class MeetingDetailBasicVO {

@Tolerate
public MeetingDetailBasicVO() {
}

private Long id;

@ApiModelProperty("会议名称")
private String name;

@ApiModelProperty("会议类型名称")
private String typeName;

@ApiModelProperty("会议类型代码")
private String type;

@ApiModelProperty("开始时间")
@JSONField(format = "yyyy-MM-dd HH:mm")
private LocalDateTime startTime;

@ApiModelProperty("结束时间")
@JSONField(format = "yyyy-MM-dd HH:mm")
private LocalDateTime endTime;

@ApiModelProperty("联系人")
private String connecter;

@ApiModelProperty("创建人")
private String author;

private List<RegionDTO> regions;

@ApiModelProperty("地点")
private String address;

@ApiModelProperty("联系方式")
private String contact;

@ApiModelProperty("创建时间")
private LocalDateTime createOn;

@ApiModelProperty("会议说明")
private String description;

@ApiModelProperty("相关材料")
private List<AttachFileVo> attachments;

@ApiModelProperty("备注")
private String remark;

@ApiModelProperty("会议状态")
private Integer status;

@ApiModelProperty("专家出席状态")
private Integer attendStatus;

@ApiModelProperty("取消说明")
private String cancelRemark;

@ApiModelProperty("创建人")
private String createBy;

private Boolean invited;

@ApiModelProperty("抽取单位")
private String inviterCompany;

@ApiModelProperty("是否已确认下发会议通知名单")
private Boolean sendMeetingNotice;

}

+ 29
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingResultVO.java 파일 보기

@@ -0,0 +1,29 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.ningdatech.file.entity.vo.result.AttachFileVo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
* <p>
* MeetingResultVo
* </p>
*
* @author WendyYang
* @since 21:28 2022/8/8
*/
@Data
public class MeetingResultVO {

@ApiModelProperty("事务ID")
private Long meetingId;

@ApiModelProperty("会议结果说明")
private String resultDescription;

@ApiModelProperty("附件信息")
private List<AttachFileVo> attachments;

}

+ 31
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/RandomInviteRuleVO.java 파일 보기

@@ -0,0 +1,31 @@
package com.ningdatech.pmapi.meeting.entity.vo;

import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.sys.model.dto.RegionDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.List;

/**
* <p>
* RandomInviteRuleVo
* </p>
*
* @author WendyYang
* @since 09:55 2022/8/26
*/
@Data
@ApiModel("随机抽取规则视图实体")
@EqualsAndHashCode(callSuper = true)
public class RandomInviteRuleVO extends RandomInviteRuleDTO {

@ApiModelProperty("履职意向地(回显使用)")
private List<RegionDTO> intentionRegions;

@ApiModelProperty("专家层级意向地(回显使用)")
private List<RegionDTO> expertRegions;

}

+ 80
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java 파일 보기

@@ -0,0 +1,80 @@
package com.ningdatech.pmapi.meeting.helper;

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.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.enumeration.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

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

/**
* <p>
* ExpertInviteHelper
* </p>
*
* @author WendyYang
* @since 16:35 2022/11/21
*/
@Slf4j
@Component
@AllArgsConstructor
public class ExpertInviteHelper {

private final IMeetingService meetingService;
private final IMeetingExpertService meetingExpertService;

/**
* 获取时间段内被抽中的专家(通知中、确认参加)
*
* @param start 开始时间
* @param end 结束时间
* @return java.util.List<java.lang.Long>
* @author WendyYang
**/
public List<Long> listInvitedExpertByTime(LocalDateTime start, LocalDateTime end) {
LambdaQueryWrapper<Meeting> meetingQuery = Wrappers.lambdaQuery(Meeting.class)
.select(Meeting::getId)
.eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode())
.and(wrapper -> wrapper.between(Meeting::getStartTime, start, end)
.or(wrapper1 -> wrapper1.between(Meeting::getEndTime, start, end)));
List<Meeting> meetings = meetingService.list(meetingQuery);
if (meetings.isEmpty()) {
return Collections.emptyList();
}
List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
LambdaQueryWrapper<MeetingExpert> meetingExpertQuery = Wrappers.lambdaQuery(MeetingExpert.class)
.select(MeetingExpert::getExpertId)
.in(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode(), ExpertAttendStatus.NOTICING.getCode())
.in(MeetingExpert::getMeetingId, meetingIds);
List<MeetingExpert> meetingExperts = meetingExpertService.list(meetingExpertQuery);
return CollUtils.fieldList(meetingExperts, MeetingExpert::getExpertId);
}

public Set<Long> listExpertLeaveOrInvited(LocalDateTime start, LocalDateTime end) {
Set<Long> notInUserIds = new HashSet<>();
notInUserIds.addAll(listInvitedExpertByTime(start, end));
return notInUserIds;
}

public Set<Long> getAvoidExpert(List<Long> appoints, AvoidInfoDTO avoid, LocalDateTime start, LocalDateTime end) {
Set<Long> expertIds = new HashSet<>();
Optional.ofNullable(appoints).ifPresent(expertIds::addAll);
Optional.ofNullable(avoid)
.flatMap(w -> Optional.ofNullable(w.getExpertIds()))
.ifPresent(expertIds::addAll);
// 过滤掉请假专家
expertIds.addAll(listInvitedExpertByTime(start, end));
return expertIds;
}

}

+ 222
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java 파일 보기

@@ -0,0 +1,222 @@
package com.ningdatech.pmapi.meeting.helper;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ningdatech.basic.exception.BizException;
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.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.model.dto.ExpertDictionaryDTO;
import com.ningdatech.pmapi.expert.model.dto.ExpertUserFullInfoDTO;
import com.ningdatech.pmapi.expert.service.ExpertInfoService;
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.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus;
import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq;
import com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVO;
import com.ningdatech.pmapi.meeting.entity.vo.MeetingByManagerVO;
import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meta.constant.DictExpertInfoTypeEnum;
import com.ningdatech.pmapi.meta.helper.DictionaryCache;
import com.ningdatech.pmapi.meta.model.dto.DictionaryDTO;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.*;

/**
* <p>
* MeetingManageHelper
* </p>
*
* @author WendyYang
* @since 17:23 2022/8/23
*/
@Component
@AllArgsConstructor
public class MeetingManageHelper {

private final DictionaryCache dictionaryCache;
private final ExpertInfoService expertInfoService;
private final IExpertUserFullInfoService expertUserFullInfoService;
private final IMeetingExpertService meetingExpertService;
private final IExpertInviteRuleService inviteRuleService;
private final IExpertInviteAvoidRuleService inviteAvoidRuleService;

/**
* 获取专家出席会议的状态
*
* @param info 会议状态及评价信息
* @return java.lang.Integer
* @author WendyYang
**/
public Integer getExpertAttendStatus(MeetingAndAttendStatusDTO info) {
if (info.getAttended() == null && info.getStatus().equals(ExpertAttendStatus.AGREED.getCode())) {
return MeetingStatus.Expert.TO_ATTEND.getCode();
} else if (info.getStatus().equals(ExpertAttendStatus.ON_LEAVE.getCode())) {
return MeetingStatus.Expert.ON_LEAVE.getCode();
} else if (info.getAttended() != null && info.getAttended()) {
return MeetingStatus.Expert.ATTENDED.getCode();
} else {
return MeetingStatus.Expert.UN_ATTEND.getCode();
}
}

public MeetingByManagerVO buildByMeeting(Meeting meeting) {
return MeetingByManagerVO.builder()
.id(meeting.getId())
.dateTypeName(dictionaryCache.getByCode(meeting.getType()).getName())
.meetingAddress(meeting.getRegionDetail())
.name(meeting.getName())
.startTime(meeting.getStartTime())
.endTime(meeting.getEndTime())
.connecter(meeting.getConnecter())
.contact(meeting.getContact())
.status(meeting.getStatus())
.invited(meeting.getInvited())
.inviterCompany(meeting.getHoldCompany())
.build();
}

public void buildMeetingQuery(LambdaQueryWrapper<Meeting> query, MeetingListReq po) {
if (StrUtil.isNotBlank(po.getName())) {
query.like(Meeting::getName, po.getName());
}
if (StrUtil.isNotBlank(po.getAddress())) {
query.like(Meeting::getRegionDetail, po.getAddress());
}
if (StrUtil.isNotBlank(po.getType())) {
query.eq(Meeting::getType, po.getType());
}
if (po.getStartTime() != null) {
query.ge(Meeting::getStartTime, po.getStartTime());
}
if (po.getEndTime() != null) {
query.le(Meeting::getEndTime, po.getEndTime());
}
}

/**
* 构建用户基本信息
*
* @param userIds 用户ID
* @return java.util.Map<java.lang.Long, com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVo>
* @author WendyYang
**/
public Map<Long, ExpertBasicInfoVO> getExpertBasicInfo(List<Long> userIds) {
/*List<ExpertFullInfoAllDTO> expertInfos = expertInfoService.listExpertUserFullInfoAll(userIds);
return CollUtils.listToMap(expertInfos, ExpertFullInfoAllDTO::getUserId, w -> {
ExpertBasicInfoVO basicInfoVo = new ExpertBasicInfoVO();
ExpertUserFullInfoDTO userInfo = w.getExpertUserInfoDTO();
basicInfoVo.setName(userInfo.getName());
basicInfoVo.setExpertId(w.getUserId());
basicInfoVo.setContact(userInfo.getPhoneNo());
basicInfoVo.setCompany(userInfo.getCompany());
Optional<ExpertDictionaryDTO> first = w.getExpertDictionaryList().stream()
.filter(dict -> dict.getExpertInfoField().equals(DictExpertInfoTypeEnum.TITLE_LEVEL.getKey()))
.findFirst();
if (first.isPresent()) {
DictionaryDTO dictInfo = dictionaryCache.getByCode(first.get().getDictionaryCode());
basicInfoVo.setJobLevel(dictInfo.getName());
} else {
basicInfoVo.setJobLevel("");
}
basicInfoVo.setCompanyType("");
return basicInfoVo;
});*/
// TODO
return null;
}

public AvoidInfoDTO getAvoidInfoDto(Long meetingId) {
ExpertInviteAvoidRule avoidRule = inviteAvoidRuleService.getByMeetingId(meetingId);
AvoidInfoDTO avoidInfoDto = new AvoidInfoDTO();
avoidInfoDto.setAvoidMates(avoidRule.getAvoidMates());
avoidInfoDto.setExpertIds(BizUtils.splitToLong(avoidRule.getExpertIds()));
avoidInfoDto.setCompanyIds(StrUtils.split(avoidRule.getCompanyIds()));
return avoidInfoDto;
}

public void saveAvoidInfo(Long meetingId, List<Long> expertIds) {
ExpertInviteAvoidRule avoidRule = inviteAvoidRuleService.getByMeetingId(meetingId);
avoidRule.setExpertIds(CollUtils.joinByComma(expertIds));
inviteAvoidRuleService.saveOrUpdate(avoidRule);
}

/**
* 校验是否能够进行指定邀请
*
* @param meetingId 会议ID
* @param expertIds 专家ID
* @author WendyYang
**/
public List<ExpertUserFullInfo> appointExpertCheck(Long meetingId, List<Long> expertIds) {
List<ExpertUserFullInfo> experts = expertUserFullInfoService.listByUserId(expertIds);
AvoidInfoDTO avoidRule = getAvoidInfoDto(meetingId);
Map<String, Integer> countMap = new HashMap<>(16);
experts.forEach(expert -> {
if (avoidRule.getCompanyIds().contains(expert.getCompany())) {
throw BizException.wrap("回避单位的专家不可出现在指定邀请名单中");
}
if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds()) && avoidRule.getExpertIds().contains(expert.getUserId())) {
throw BizException.wrap("已回避的专家不可被指定邀请");
}
if (avoidRule.getAvoidMates()) {
Integer oldValue = countMap.put(expert.getCompany(), 1);
Assert.isNull(oldValue, "不可选择同单位的多个专家");
}
// 校验专家状态
ExpertAccountStatusEnum accountStatus = ExpertAccountStatusEnum.of(expert.getExpertAccountStatus());
if (!accountStatus.equals(ExpertAccountStatusEnum.AVAILABLE)) {
throw BizException.wrap("专家%s不可被抽取", expert.getExpertName());
}
});
Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(experts, ExpertUserFullInfo::getUserId);
List<MeetingExpert> meetingExperts = meetingExpertService.listByMeetingId(meetingId);
if (!meetingExperts.isEmpty()) {
Comparator<MeetingExpert> comparator = Comparator.comparing(MeetingExpert::getUpdateOn).reversed();
Collection<MeetingExpert> values = BizUtils.groupFirst(meetingExperts, MeetingExpert::getExpertId, comparator);
values.forEach(w -> {
ExpertUserFullInfo expertInfo = expertMap.get(w.getExpertId());
if (expertInfo != null) {
String expertName = expertInfo.getExpertName();
switch (ExpertAttendStatus.getByCode(w.getStatus())) {
case REFUSED:
throw BizException.wrap("专家%s已拒绝参加", expertName);
case CANCELED:
throw BizException.wrap("专家%s已被移除", expertName);
case REPLACED:
switch (ExpertAttendStatus.getByCode(w.getPreStatus())) {
case REFUSED:
throw BizException.wrap("专家%s已拒绝参加", expertName);
case CANCELED:
throw BizException.wrap("专家%s已被移除", expertName);
default:
break;
}
break;
case AGREED:
throw BizException.wrap("专家%s已同意参加", expertName);
case NOTICING:
throw BizException.wrap("专家%s正在通知中", expertName);
default:
break;
}
}
});
}
return experts;
}

}

+ 50
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java 파일 보기

@@ -0,0 +1,50 @@
package com.ningdatech.pmapi.meeting.helper;

import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;

/**
* <p>
* YxtCallOrSmsHelper
* </p>
*
* @author WendyYang
* @since 23:59 2022/8/31
*/
@Component
@AllArgsConstructor
public class YxtCallOrSmsHelper {

// private final YinXinTongClient yinXinTongClient;

public void callByMeetingExperts(Meeting meeting, List<MeetingExpert> experts) {
/*String callContent = String.format(YxtCallTemplateConst.OFFLINE_TEMPLATE,
meeting.getHoldCompany(), meeting.getName(),
meeting.getStartTime().format(DateUtil.DTF_YMD_HM), meeting.getRegionDetail());
List<SubmitTaskCallContext> callContexts = CollUtils.convert(experts, w -> {
SubmitTaskCallContext context = new SubmitTaskCallContext();
context.setContent(callContent);
context.setReceiveNumber(w.getMobile());
return context;
});
SubmitTaskCallResponse callResponse = yinXinTongClient.submitTaskCall(SubmitTaskCallCmd.of(callContexts));
experts.forEach(w -> w.setSubmitKey(callResponse.getSubmitKey()));*/
}

/*public void sendSms(List<SendSmsContext> smsList) {
yinXinTongClient.submitSmsTask(new SendSmsCmd() {{
setContextList(smsList);
setSmsSignEnum(YxtSmsSignEnum.ZJS_ELECTRONIC_EXPERT_LIB);
}});
}

public void sendSms(SendSmsContext sms) {
sendSms(Collections.singletonList(sms));
}*/

}

+ 241
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/DashboardManage.java 파일 보기

@@ -0,0 +1,241 @@
package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.PagePo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard;
import com.ningdatech.pmapi.meeting.entity.req.MeetingCalenderReq;
import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq;
import com.ningdatech.pmapi.meeting.entity.vo.*;
import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.meta.helper.DictionaryCache;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Component;

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

/**
* <p>
* DashboardManage
* </p>
*
* @author WendyYang
* @since 23:04 2022/8/24
*/
@Component
@AllArgsConstructor
public class DashboardManage {

private final MeetingManage meetingManage;
private final IMeetingService meetingService;
private final IMeetingExpertService meetingExpertService;
private final MeetingManageHelper meetingManageHelper;
private final IMeetingExpertEvaluationService meetingExpertEvaluationService;
private final DictionaryCache dictionaryCache;

public static final int MEETING_CALENDER_MONTHS = 2;

public List<MeetingCalenderItemVO> meetingCalender(MeetingCalenderReq po) {
if (po.getStartDate().plusMonths(MEETING_CALENDER_MONTHS).isBefore(po.getEndDate())) {
throw new BizException("最多可查询两个月数据");
}
MeetingListReq meetingListPo = new MeetingListReq();
meetingListPo.setPageSize(500);
meetingListPo.setStartTime(po.getStartDate().atStartOfDay());
meetingListPo.setEndTime(po.getEndDate().atTime(LocalTime.MAX));
PageVo<MeetingByManagerVO> meetingPage = meetingManage.meetingListByExpert(meetingListPo);
Map<LocalDate, List<MeetingByManagerVO>> meetingByDate = meetingPage.getRecords().stream().map(w -> {
List<Pair<LocalDate, MeetingByManagerVO>> pairs = new ArrayList<>();
LocalDateTime tempTime = w.getStartTime();
while (tempTime.isBefore(w.getEndTime())) {
pairs.add(Pair.of(tempTime.toLocalDate(), w));
tempTime = tempTime.plusDays(1);
}
return pairs;
}).flatMap(Collection::stream)
.collect(Collectors.groupingBy(Pair::getLeft,
Collectors.collectingAndThen(Collectors.mapping(Pair::getRight, Collectors.toList()),
w -> {
w.sort(Comparator.comparing(MeetingByManagerVO::getStartTime));
return w;
})));
LocalDate tempDate = po.getStartDate();
List<MeetingCalenderItemVO> result = new ArrayList<>();
while (!tempDate.isAfter(po.getEndDate())) {
MeetingCalenderItemVO item = new MeetingCalenderItemVO();
List<MeetingByManagerVO> meetings = meetingByDate.get(tempDate);
item.setHasMeeting(meetings != null);
if (item.getHasMeeting()) {
item.setMeetings(meetings);
}
item.setToday(tempDate);
result.add(item);
tempDate = tempDate.plusDays(1);
}
return result;
}

public PageVo<ExpertEvaluationToDoListItemVO> expertEvaluationToDo(PagePo po) {
PageVo<ExpertEvaluationToDoListItemVO> result = PageVo.of(new ArrayList<>(), 0L);
// 查询所有未完成的项目
LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
.select(Meeting::getId)
.ne(Meeting::getStatus, MeetingStatus.Manager.CANCELED.getCode())
.eq(Meeting::getCreateBy, LoginUserUtil.getUserId());
List<Meeting> meetings = meetingService.list(query);
if (meetings.isEmpty()) {
return PageVo.empty();
}
List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
Page<MeetingExpert> page = meetingExpertEvaluationService.pageExpertEvaluationTodo(meetingIds, po);
if (page.getTotal() > 0) {
List<Long> expertIds = new ArrayList<>(), meetingIdsByPage = new ArrayList<>();
page.getRecords().forEach(w -> {
meetingIdsByPage.add(w.getMeetingId());
expertIds.add(w.getExpertId());
});
Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetingService.listByIds(meetingIdsByPage), Meeting::getId);
Map<Long, ExpertBasicInfoVO> basicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds);
page.getRecords().forEach(w -> {
ExpertBasicInfoVO expertInfo = basicInfoVoMap.get(w.getExpertId());
Meeting meeting = meetingMap.get(w.getMeetingId());
ExpertEvaluationToDoListItemVO item = BeanUtil.copyProperties(expertInfo, ExpertEvaluationToDoListItemVO.class);
item.setMeetingName(meeting.getName());
item.setMeetingId(w.getMeetingId());
item.setExpertMeetingId(w.getId());
item.setStartTime(meeting.getStartTime());
item.setEndTime(meeting.getEndTime());
result.getRecords().add(item);
});
result.setTotal(page.getTotal());
}
return result;
}

public PageVo<MeetingConfirmToDoListItemVO> expertConfirmToDo(PagePo po) {
// 查询所有未完成的项目
LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
.eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode())
.eq(Meeting::getCreateBy, LoginUserUtil.getUserId())
.orderByDesc(Meeting::getStartTime);
List<Meeting> meetings = meetingService.list(query);
if (meetings.isEmpty()) {
return PageVo.empty();
}
List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
Map<Long, CountConfirmByMeetingIdDTO> confirmedMap = meetingExpertService.countConfirmedByMeetingIds(meetingIds);
confirmedMap.entrySet().removeIf(w -> {
CountConfirmByMeetingIdDTO confirm = w.getValue();
return confirm.getConfirmed().equals(confirm.getTotal());
});
if (confirmedMap.size() == 0) {
return PageVo.empty();
}
meetings.removeIf(w -> !confirmedMap.containsKey(w.getId()));
List<MeetingConfirmToDoListItemVO> dataList = meetings.stream()
.skip((long) (po.getPageNumber() - 1) * po.getPageSize())
.limit(po.getPageSize()).map(w -> {
MeetingConfirmToDoListItemVO item = new MeetingConfirmToDoListItemVO();
CountConfirmByMeetingIdDTO confirm = confirmedMap.get(w.getId());
if (confirm == null) {
item.setTotalExpert(0);
item.setConfirmedExpert(0);
} else {
item.setTotalExpert(confirm.getTotal());
item.setConfirmedExpert(confirm.getConfirmed());
}
item.setMeetingId(w.getId());
item.setEndTime(w.getEndTime());
item.setStartTime(w.getStartTime());
item.setStatus(w.getStatus());
item.setRegionDetail(w.getRegionDetail());
item.setType(dictionaryCache.getByCode(w.getType()).getName());
item.setMeetingName(w.getName());
return item;
}).collect(Collectors.toList());
return PageVo.of(dataList, meetings.size());
}

public PageVo<ExpertReplaceTodoListItemVO> expertReplaceTodoList(PagePo po) {
// 查询所有未完成的项目
LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
.eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode())
.eq(Meeting::getCreateBy, LoginUserUtil.getUserId())
.orderByDesc(Meeting::getStartTime);
List<Meeting> meetings = meetingService.list(query);
if (meetings.isEmpty()) {
return PageVo.empty();
}
List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
Page<MeetingExpert> page = meetingExpertService.pageExpertByStatusAndMeetingIds(new Page<>(po.getPageNumber(), po.getPageSize()),
meetingIds, ExpertAttendStatus.ON_LEAVE);
if (page.getTotal() == 0) {
return PageVo.empty();
}
Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetings, Meeting::getId);
List<Long> expertIds = CollUtils.fieldList(page.getRecords(), MeetingExpert::getExpertId);
Map<Long, ExpertBasicInfoVO> basicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds);
List<ExpertReplaceTodoListItemVO> dataList = page.getRecords().stream().map(w -> {
ExpertBasicInfoVO basicInfoVo = basicInfoVoMap.get(w.getExpertId());
ExpertReplaceTodoListItemVO item = BeanUtil.copyProperties(basicInfoVo, ExpertReplaceTodoListItemVO.class);
item.setMeetingId(w.getMeetingId());
item.setExpertMeetingId(w.getId());
Meeting meeting = meetingMap.get(w.getMeetingId());
item.setMeetingName(meeting.getName());
item.setInviteType(ExpertInviteType.getByCode(w.getInviteType()).getName());
item.setStatus(w.getStatus());
item.setStartTime(meeting.getStartTime());
item.setEndTime(meeting.getEndTime());
return item;
}).collect(Collectors.toList());
return PageVo.of(dataList, page.getTotal());
}

public MeetingCountSummaryVO meetingCountSummary() {
Map<MeetingStatusByDashboard, Integer> meetingCountSummary = meetingService.meetingCountSummary(LoginUserUtil.getUserId());
return MeetingCountSummaryVO.builder()
.canceled(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.CANCELED, 0))
.completed(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.COMPLETED, 0))
.toBeHeld(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.TO_BE_HELD, 0))
.held(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.HELD, 0))
.build();
}

public MeetingCountByExpertVO meetingCountByExpert() {
List<MeetingAndAttendStatusDTO> attendStatusList = meetingExpertService.listByExpertIdAndStatus(LoginUserUtil.getUserId(), null, null);
MeetingCountByExpertVO result = MeetingCountByExpertVO.init();
attendStatusList.forEach(w -> {
if (w.getStatus().equals(ExpertAttendStatus.ON_LEAVE.getCode())) {
result.incrLeaved();
} else if (w.getAttended() != null && w.getAttended()) {
result.incrAttended();
} else {
result.incrToBeAttended();
}
});
return result;
}


}

+ 672
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java 파일 보기

@@ -0,0 +1,672 @@
package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject;
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.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.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper;
import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
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.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

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

/**
* <p>
* 专家邀请管理
* </p>
*
* @author WendyYang
* @since 18:05 2022/8/8
*/
@Component
@AllArgsConstructor
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 iExpertAvoidCompanyService;
private final YxtCallOrSmsHelper yxtCallOrSmsHelper;

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::getPhoneNo)
.eq(ExpertUserFullInfo::getExpertAccountStatus, ExpertAccountStatusEnum.AVAILABLE.getKey());
}

/**
* 增加专家层级限制
*
* @param query 查询
* @param rule 随机邀请规则
*/
private static void addRegionLimit(LambdaQueryWrapper<ExpertUserFullInfo> query, RandomInviteRuleDTO rule) {
if (ObjectUtils.allNotNull(rule.getExpertRegionCode(), rule.getExpertRegionLevel())) {
query.eq(ExpertUserFullInfo::getRegionCode, rule.getExpertRegionCode());
query.eq(ExpertUserFullInfo::getRegionLevel, rule.getExpertRegionLevel());
}
}

/**
* 获取满足履职意向地的专家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 companyNames /
* @return /
*/
private List<Long> avoidCompanyExpertIds(List<String> companyNames) {
if (CollUtil.isEmpty(companyNames)) {
return new ArrayList<>();
}
List<String> dealCompanyNames = new ArrayList<>();
for (String companyName : companyNames) {
if (companyName.contains(",")) {
String[] splitCompanyNames = companyName.split(",");
for (String splitCompanyName : splitCompanyNames) {
dealCompanyNames.add(splitCompanyName);
}
} else {
dealCompanyNames.add(companyName);
}
}
List<ExpertAvoidCompany> expertAvoidCompanyList = iExpertAvoidCompanyService.list(Wrappers.lambdaQuery(ExpertAvoidCompany.class)
.in(ExpertAvoidCompany::getCompanyName, dealCompanyNames));
return expertAvoidCompanyList.stream().map(ExpertAvoidCompany::getUserId).distinct().collect(Collectors.toList());
}

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

// 聚合用户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 null;
}
}

if (CollUtil.isNotEmpty(avoidCompanyExpertIds)) {
for (Long avoidCompanyExpertId : avoidCompanyExpertIds) {
expertIdsIn.remove(avoidCompanyExpertId);
}
}
return expertIdsIn;
}

/**
* 根据专家标签获取满足的专家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 avoidInfo 回避信息
* @param randomRule 抽取规则
* @param appointExpertIds 指定抽取专家ID
* @return 满足抽取条件的专家
* @author WendyYang
**/
public ExpertChooseDTO expertInviteByRandomRule(AvoidInfoDTO avoidInfo,
RandomInviteRuleDTO randomRule,
List<Long> appointExpertIds,
LocalDateTime start,
LocalDateTime end) {
ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidInfo);
if (expertIdsIn == null) {
return result;
}
boolean avoid = avoidInfo != null;
boolean avoidExpert = avoid && CollUtil.isNotEmpty(avoidInfo.getExpertIds());
boolean avoidCompany = avoid && CollUtil.isNotEmpty(avoidInfo.getCompanyIds());
Set<String> tmpAvoidCompany = new HashSet<>();
if (avoidCompany) {
List<String> companyIds = avoidInfo.getCompanyIds();
for (String companyId : companyIds) {
if (companyId.contains(",")) {
String[] splitCompanyIds = companyId.split(",");
for (String splitCompanyId : splitCompanyIds) {
tmpAvoidCompany.add(splitCompanyId);
}
} else {
tmpAvoidCompany.add(companyId);
}
}
}
// 回避信息
if (CollUtil.isNotEmpty(appointExpertIds) && avoid && avoidInfo.getAvoidMates()) {
// 如果设置了回避本单位同事且手动指定了专家,则对应的单位需要排除
List<ExpertUserFullInfo> appointUsers = expertUserFullInfoService.listByUserId(appointExpertIds);
tmpAvoidCompany.addAll(CollUtils.fieldList(appointUsers, ExpertUserFullInfo::getCompany));
}
LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
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.getCompanyIds()));
}
// 处理专家层级
addRegionLimit(query, randomRule);

if (!expertIdsIn.isEmpty()) {
if (avoidExpert) {
expertIdsIn.removeIf(w -> avoidInfo.getExpertIds().contains(w));
if (expertIdsIn.isEmpty()) {
// 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID
// 如果为空则说明没有符合条件的
return result;
}
}
if (CollUtil.isNotEmpty(appointExpertIds)) {
expertIdsIn.removeIf(appointExpertIds::contains);
if (expertIdsIn.isEmpty()) {
return result;
}
}
// 过滤掉已参加会议的专家
List<Long> expertIdsLockByMeeting = expertInviteHelper.listInvitedExpertByTime(start, end);
expertIdsIn.removeIf(expertIdsLockByMeeting::contains);
if (expertIdsIn.isEmpty()) {
return result;
}
query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
} else if (avoidExpert || CollUtil.isNotEmpty(appointExpertIds)) {
Set<Long> tempExperts = expertInviteHelper.getAvoidExpert(appointExpertIds, avoidInfo, start, end);
query.notIn(ExpertUserFullInfo::getUserId, tempExperts);
} else {
Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end);
if (!notInUserIds.isEmpty()) {
query.notIn(ExpertUserFullInfo::getUserId, notInUserIds);
}
}
List<ExpertUserFullInfo> userFullInfos = expertUserFullInfoService.list(query);
if (userFullInfos.isEmpty()) {
return result;
}
if (avoid && avoidInfo.getAvoidMates()) {
// 回避同单位其他专家
Map<String, List<ExpertUserFullInfo>> userFullInfoMap = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompany);
result.setTotal(userFullInfoMap.size());
if (randomRule.getCount() == null || userFullInfoMap.size() >= randomRule.getCount()) {
List<ExpertUserFullInfo> userFullInfoList = inviteGroupByCompany(userFullInfoMap, randomRule.getCount());
result.setExperts(userFullInfoList);
}
} else {
result.setTotal(userFullInfos.size());
// count为空表示数量校验
if (randomRule.getCount() == null || result.getTotal() >= randomRule.getCount()) {
List<ExpertUserFullInfo> userFullInfoList = inviteWithoutCompany(userFullInfos, randomRule.getCount());
result.setExperts(userFullInfoList);
}
}
return result;
}

/**
* 专家替换、补充
*
* @param avoidInfo 回避信息
* @param randomRule 随机抽取
* @param meetingExperts 已抽取人员
* @param count 抽取数量
* @return com.ningdatech.emapi.meeting.entity.dto.ExpertChooseDto
* @author WendyYang
**/
public ExpertChooseDTO expertReplaceByRandomRule(AvoidInfoDTO avoidInfo,
RandomInviteRuleDTO randomRule,
Collection<MeetingExpert> meetingExperts,
Integer count,
LocalDateTime start,
LocalDateTime end,
Long replacedExpertId) {
ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
// 合并标签、字典
List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidInfo);
if (expertIdsIn == null) {
return result;
}
LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
query.notIn(ExpertUserFullInfo::getCompany, avoidInfo.getCompanyIds());
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.getCompanyIds()));

// 处理专家层级
if (ObjectUtils.allNotNull(randomRule.getExpertRegionCode(), randomRule.getExpertRegionLevel())) {
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 (expertIdsIn.isEmpty()) {
// 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID
// 如果为空则说明没有符合条件的
return result;
}
}
List<Long> lockExpertIds = expertInviteHelper.listInvitedExpertByTime(start, end);
expertIdsIn.removeIf(lockExpertIds::contains);
if (expertIdsIn.isEmpty()) {
return result;
}
query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
} else if (CollectionUtils.isNotEmpty(avoidInfo.getExpertIds())) {
Set<Long> notInExpertIds = expertInviteHelper.getAvoidExpert(avoidInfo.getExpertIds(), null, start, end);
query.notIn(ExpertUserFullInfo::getUserId, notInExpertIds);
} else {
Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end);
if (notInUserIds.size() > 0) {
query.notIn(ExpertUserFullInfo::getUserId, notInUserIds);
}
}
List<ExpertUserFullInfo> userFullInfos = expertUserFullInfoService.list(query);
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<ExpertAttendStatus, List<MeetingExpert>> expertIdGroupByStatus = tempExpertIdsMap.values().stream()
.collect(Collectors.groupingBy(w -> ExpertAttendStatus.getByCode(w.getStatus())));
// 回避同单位其他专家
List<MeetingExpert> removeExpertByCompany = new ArrayList<>();
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.AGREED), removeExpertByCompany::addAll);
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.NOTICING), removeExpertByCompany::addAll);
List<Long> removeExpertIds = new ArrayList<>();
// 拒绝参加的不可以被再次抽中
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.REFUSED), w -> {
List<Long> tempRefused = CollUtils.fieldList(w, MeetingExpert::getExpertId);
removeExpertIds.addAll(tempRefused);
});
// 被取消的也不可以被再次抽中
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.CANCELED), w -> {
List<Long> tempCanceled = CollUtils.fieldList(w, MeetingExpert::getExpertId);
removeExpertIds.addAll(tempCanceled);
});
// 被替换之前是上述两种状态的不可被再次抽中
BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.REPLACED), w -> {
for (MeetingExpert me : w) {
BizUtils.notNull(me.getPreStatus(), preStatus -> {
if (ExpertAttendStatus.REFUSED.eq(preStatus) || ExpertAttendStatus.CANCELED.eq(preStatus)) {
removeExpertIds.add(me.getExpertId());
}
});
}
});
// 不为空时表示单个专家替换否则为专家补抽
if (replacedExpertId != null) {
removeExpertIds.add(replacedExpertId);
}
List<Long> tempExpertIds = CollUtils.fieldList(removeExpertByCompany, MeetingExpert::getExpertId);
if (avoidInfo.getAvoidMates()) {
if (!tempExpertIds.isEmpty()) {
Set<String> confirmedCompany = userFullInfos.stream()
.filter(w -> tempExpertIds.contains(w.getUserId()))
.map(ExpertUserFullInfo::getCompany).collect(Collectors.toSet());
userFullInfos.removeIf(w -> confirmedCompany.contains(w.getCompany()));
}
userFullInfos.removeIf(w -> removeExpertIds.contains(w.getUserId()));
Map<String, List<ExpertUserFullInfo>> groupByCompany = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompany);
result.setTotal(groupByCompany.size());
result.setExperts(inviteGroupByCompany(groupByCompany, count));
} else {
// 移除确认参加、通知中的、拒绝参加、已取消
userFullInfos.removeIf(w -> tempExpertIds.contains(w.getUserId()) || removeExpertIds.contains(w.getUserId()));
result.setTotal(userFullInfos.size());
result.setExperts(inviteWithoutCompany(userFullInfos, count));
}
return result;
}

private List<List<MeetingExpert>> selectMeetingExpertByCount() {
LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
.select(Meeting::getId)
.orderByDesc(Meeting::getCreateOn)
.last("limit " + 5);
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 expertGroupByCompany 需要抽取的人
* @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)) {
return Collections.emptyList();
}
List<List<MeetingExpert>> meetingExperts = selectMeetingExpertByCount();
if (meetingExperts.isEmpty()) {
return expertGroupByCompany.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());
for (int i = 0; i < count; i++) {
String key = keySet.get(RandomUtils.nextInt(0, keySet.size()));
List<ExpertUserFullInfo> expertUserFullInfos = expertGroupByCompany.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 inviteRule 邀请规则
* @param meetingId 会议ID
* @return {@link ExpertInviteRule}
* @author WendyYang
**/
private static ExpertInviteRule getExpertInviteRule(AbstractInviteRule inviteRule, Long meetingId) {
ExpertInviteRule rule = new ExpertInviteRule();
rule.setInviteRule(JSONObject.toJSONString(inviteRule));
rule.setInviteCount(inviteRule.getCount());
rule.setInviteType(inviteRule.getInviteType());
rule.setMeetingId(meetingId);
return rule;
}

public Map<Long, ExpertUserFullInfo> checkAppointExpertConflictWithAvoidRule(AppointInviteRuleDTO appointRule, AvoidInfoDTO avoidRule) {
if (appointRule == null) {
return Collections.emptyMap();
}
List<ExpertUserFullInfo> expertInfos = expertUserFullInfoService.listByUserId(appointRule.getExpertIds());
Map<Long, ExpertUserFullInfo> infoMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId);
if (avoidRule == null) {
return infoMap;
}
Map<String, Integer> countMap = new HashMap<>(16);
infoMap.forEach((key, userFullInfo) -> {
if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds())) {
boolean contains = avoidRule.getExpertIds().contains(key);
Assert.isTrue(!contains, "已回避的专家不可被指定邀请");
}
if (CollectionUtils.isNotEmpty(avoidRule.getCompanyIds())) {
boolean contains = avoidRule.getCompanyIds().contains(userFullInfo.getCompany());
Assert.isTrue(!contains, "回避单位的专家不可出现在指定邀请名单中");
}
if (avoidRule.getAvoidMates()) {
Integer oldValue = countMap.put(userFullInfo.getCompany(), 1);
Assert.isNull(oldValue, "不可选择同单位的多个专家");
}
});
return infoMap;
}


/**
* 专家抽取(会议创建时抽取)
*
* @param randomRules 随机抽取规则
* @param appointRule 指定抽取规则
* @param avoid 回避信息
* @author WendyYang
**/
public void expertInviteByMeetingCreate(Meeting meeting,
List<RandomInviteRuleDTO> randomRules,
AppointInviteRuleDTO appointRule,
AvoidInfoDTO avoid) {
Map<Long, ExpertUserFullInfo> appointUser = checkAppointExpertConflictWithAvoidRule(appointRule, avoid);
List<MeetingExpert> expertInserts = new ArrayList<>();
boolean appointed = appointRule != null;
// 处理随机抽取规则
if (CollectionUtils.isNotEmpty(randomRules)) {
List<ExpertInviteRule> randoms = new ArrayList<>();
List<ExpertChooseDTO> expertsByRandom = new ArrayList<>();
List<Long> tempExpertIdSelected = new ArrayList<>();
if (appointed) {
tempExpertIdSelected.addAll(appointRule.getExpertIds());
}
LocalDateTime startTime = meeting.getStartTime(), endTime = meeting.getEndTime();
randomRules.forEach(rule -> {
ExpertChooseDTO tempExperts = expertInviteByRandomRule(avoid, rule, tempExpertIdSelected, startTime, endTime);
expertsByRandom.add(tempExperts);
tempExpertIdSelected.addAll(CollUtils.fieldList(tempExperts.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(ExpertAttendStatus.NOTICING.getCode());
expertInserts.add(expert);
});
}
yxtCallOrSmsHelper.callByMeetingExperts(meeting, expertInserts);
}
if (appointed) {
// 处理指定抽取规则
ExpertInviteRule appoint = getExpertInviteRule(appointRule, meeting.getId());
inviteRuleService.save(appoint);
Long ruleId = appoint.getId();
appointRule.getExpertIds().forEach(w -> {
ExpertUserFullInfo info = appointUser.get(w);
MeetingExpert expert = ExpertInviteBuilder.getExpertByAppoint(meeting.getId(), info, ruleId);
expert.setStatus(ExpertAttendStatus.NOTICING.getCode());
expertInserts.add(expert);
});
}
meetingExpertService.saveBatch(expertInserts);
}

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingExpertManage.java 파일 보기

@@ -0,0 +1,25 @@
package com.ningdatech.pmapi.meeting.manage;

import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

/**
* <p>
* MeetingExpertManage
* </p>
*
* @author WendyYang
* @since 10:04 2022/8/24
*/
@Component
@AllArgsConstructor
public class MeetingExpertManage {

private final IMeetingExpertService meetingExpertService;
private final IMeetingExpertEvaluationService meetingExpertEvaluationService;
private final MeetingManageHelper meetingManageHelper;

}

+ 708
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java 파일 보기

@@ -0,0 +1,708 @@
package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
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.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.IdVo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.ValidUtil;
import com.ningdatech.cache.lock.DistributedLock;
import com.ningdatech.file.entity.vo.result.AttachFileVo;
import com.ningdatech.file.service.FileService;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.common.util.StrUtils;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.helper.PermissionCheckHelper;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.builder.ExpertInviteBuilder;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule;
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.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus.Manager;
import com.ningdatech.pmapi.meeting.entity.req.*;
import com.ningdatech.pmapi.meeting.entity.vo.*;
import com.ningdatech.pmapi.meeting.entity.vo.ExpertInviteDetailVO.ExpertAttendListItemVO;
import com.ningdatech.pmapi.meeting.entity.vo.ExpertInviteDetailVO.RandomInviteListItemVO;
import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper;
import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper;
import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
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.meeting.task.ExpertInviteTask;
import com.ningdatech.pmapi.meta.helper.DictionaryCache;
import com.ningdatech.pmapi.meta.helper.TagCache;
import com.ningdatech.pmapi.user.entity.UserInfo;
import com.ningdatech.pmapi.user.service.IUserInfoService;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;

/**
* <p>
* MeetingManage
* </p>
*
* @author WendyYang
* @since 13:35 2022/7/26
*/
@Component
@RequiredArgsConstructor
public class MeetingManage {

private final IMeetingService meetingService;
private final IExpertInviteAvoidRuleService inviteAvoidRuleService;
private final IExpertInviteRuleService inviteRuleService;
private final IExpertUserFullInfoService expertUserFullInfoService;
private final FileService fileService;
private final TagCache tagCache;
private final DictionaryCache dictionaryCache;
private final IMeetingExpertService meetingExpertService;
private final ExpertInviteManage expertInviteManage;
private final ExpertInviteTask expertInviteTask;
private final MeetingManageHelper meetingManageHelper;
private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
private final DistributedLock distributedLock;
private final PermissionCheckHelper permissionCheckHelper;
private final IUserInfoService userInfoService;

private final ExpertInviteHelper expertInviteHelper;
private static final String INVITED_RULE_CREATE = "INVITED_RULE_CREATE:";

private static final String MEETING_CREATE_KEY = "MEETING_CREATE:";

/**
* 锁定时间10 * 60秒
*/
public static final int RETRY_TIMES = 3;

public IdVo<Long> save(MeetingBasicDTO po) {
String md5ByParam = SecureUtil.md5(po.toString());
String key = MEETING_CREATE_KEY + md5ByParam;
if (!distributedLock.lock(key, RETRY_TIMES)) {
throw BizException.wrap("会议正在创建中");
}
try {
Meeting meeting = BeanUtil.copyProperties(po, Meeting.class);
meeting.setStatus(Manager.UNCOMPLETED.getCode());
if (CollectionUtils.isNotEmpty(po.getAttachmentIds())) {
meeting.setAttachment(CollUtils.joinByComma(po.getAttachmentIds()));
}
Long userId = LoginUserUtil.getUserId();
UserInfo userInfo = userInfoService.getById(userId);
meeting.setHoldCompany(po.getInviterCompany());
meeting.setAuthor(userInfo.getUsername());
meeting.setInvited(Boolean.FALSE);
meeting.setStopRandomInvite(true);
meeting.setSendMeetingNotice(false);
meetingService.save(meeting);
return IdVo.of(meeting.getId());
} finally {
distributedLock.releaseLock(key);
}
}

@Transactional(rollbackFor = Exception.class)
public void expertInviteByCreate(ExpertInviteCreateReq req) {
// 未传递指定邀请专家直接忽略
if (req.getAppointRule() != null && CollectionUtils.isEmpty(req.getAppointRule().getExpertIds())) {
Assert.isTrue(CollectionUtils.isNotEmpty(req.getRandomRules()), "未指定抽取规则");
req.setAppointRule(null);
}
String key = INVITED_RULE_CREATE + req.getMeetingId();
if (!distributedLock.lock(key, RETRY_TIMES)) {
throw BizException.wrap("不可重复进行专家抽取");
}
try {
Meeting meeting = meetingService.getById(req.getMeetingId());
Assert.isTrue(!meeting.getInvited(), "不可重复进行专家抽取");
ExpertCountOnChangeVO countOnChange = expertCountOnChange(req);
for (int i = 0; i < req.getRandomRules().size(); i++) {
Integer checkCount = countOnChange.getCountList().get(i);
Integer inviteCount = req.getRandomRules().get(i).getCount();
Assert.isTrue(checkCount >= inviteCount, "可供抽取的专家数量不足");
}
AvoidInfoDTO avoidInfo = req.getAvoidInfo();
expertInviteManage.expertInviteByMeetingCreate(meeting, req.getRandomRules(), req.getAppointRule(), avoidInfo);
LambdaUpdateWrapper<Meeting> updater = Wrappers.lambdaUpdate(Meeting.class)
.set(Meeting::getInvited, Boolean.TRUE)
.eq(Meeting::getId, req.getMeetingId());
if (CollectionUtils.isNotEmpty(req.getRandomRules())) {
expertInviteTask.addInviteExpertTaskByMeetingCreate(meeting.getId(), 5);
updater.set(Meeting::getStopRandomInvite, false);
}
meetingService.update(updater);
// 回避规则
if (avoidInfo != null) {
ExpertInviteAvoidRule avoidRule = new ExpertInviteAvoidRule();
avoidRule.setMeetingId(meeting.getId());
avoidRule.setAvoidMates(avoidInfo.getAvoidMates());
avoidRule.setCompanyIds(CollUtils.joinByComma(avoidInfo.getCompanyIds()));
if (CollectionUtils.isNotEmpty(avoidInfo.getExpertIds())) {
avoidRule.setExpertIds(CollUtils.joinByComma(avoidInfo.getExpertIds()));
}
inviteAvoidRuleService.save(avoidRule);
}
} finally {
distributedLock.releaseLock(key);
}
}

/**
* 抽取数量校验
*
* @param po 抽取参数
* @return {@link ExpertCountOnChangeVO}
* @author WendyYang
**/
public ExpertCountOnChangeVO expertCountOnChange(ExpertInviteCreateReq po) {
// 未传递指定邀请专家直接忽略
if (po.getAppointRule() != null && CollUtil.isEmpty(po.getAppointRule().getExpertIds())) {
Assert.isTrue(CollUtil.isNotEmpty(po.getRandomRules()), "未指定抽取规则");
po.setAppointRule(null);
}
boolean appointed = po.getAppointRule() != null;
expertInviteManage.checkAppointExpertConflictWithAvoidRule(po.getAppointRule(), po.getAvoidInfo());
Meeting meeting = meetingService.getById(po.getMeetingId());
ExpertCountOnChangeVO resultCount = new ExpertCountOnChangeVO();
resultCount.setStatus(true);
resultCount.setCountList(new ArrayList<>());
// 参数校验
String errMsg = ValidUtil.validFast(po, AbstractInviteRule.CountCheck.class);
if (errMsg != null) {
resultCount.setStatus(false);
resultCount.setMessage(errMsg);
return resultCount;
}
List<Long> expertIdsChoose = new ArrayList<>();
if (appointed) {
expertIdsChoose.addAll(po.getAppointRule().getExpertIds());
}
for (RandomInviteRuleDTO randomRule : po.getRandomRules()) {
ExpertChooseDTO chooseExpert = expertInviteManage.expertInviteByRandomRule(po.getAvoidInfo(),
randomRule, expertIdsChoose, meeting.getStartTime(), meeting.getEndTime());
resultCount.addCountList(chooseExpert.getTotal());
}
return resultCount;
}

/**
* 专家会议列表
*
* @param req 查询参数
* @return 会议列表
* @author WendyYang
**/
public PageVo<MeetingByManagerVO> meetingListByExpert(MeetingListReq req) {
Long expertId = req.getExpertId() != null ? req.getExpertId() : LoginUserUtil.getUserId();
List<MeetingAndAttendStatusDTO> expertDtoList = meetingExpertService.listByExpertIdAndStatus(expertId, req.getStatus(), null);
if (expertDtoList.isEmpty()) {
return PageVo.empty();
}
Map<Long, MeetingAndAttendStatusDTO> mapByMeetingId = new HashMap<>(16);
expertDtoList.forEach(w -> mapByMeetingId.put(w.getMeetingId(), w));
LambdaQueryWrapper<Meeting> query = new LambdaQueryWrapper<Meeting>()
.orderByDesc(Meeting::getCreateOn)
.in(Meeting::getId, mapByMeetingId.keySet())
.ne(Meeting::getStatus, Manager.CANCELED.getCode());
if (req.getExpertId() == null) {
meetingManageHelper.buildMeetingQuery(query, req);
}
Page<Meeting> page = meetingService.page(req.page(), query);
if (page.getTotal() == 0) {
return PageVo.empty();
}
PageVo<MeetingByManagerVO> result = new PageVo<>(new ArrayList<>(), page.getTotal());
page.getRecords().forEach(meeting -> {
MeetingByManagerVO item = meetingManageHelper.buildByMeeting(meeting);
MeetingAndAttendStatusDTO info = mapByMeetingId.get(meeting.getId());
item.setAttendStatus(meetingManageHelper.getExpertAttendStatus(info));
result.getRecords().add(item);
});
return result;
}

/**
* 管理员会议列表
*
* @param po 查询参数
* @return 会议列表
* @author WendyYang
*/
public PageVo<MeetingByManagerVO> meetingListByManager(MeetingListReq po) {
LambdaQueryWrapper<Meeting> query = new LambdaQueryWrapper<Meeting>()
.orderByDesc(Meeting::getCreateOn);
// 补充逻辑 如果拥有超级管理员权限可以看到所有事务
if (!permissionCheckHelper.isSuperAdmin()) {
query.eq(Meeting::getCreateBy, LoginUserUtil.getUserId());
}
query.eq(po.getStatus() != null, Meeting::getStatus, po.getStatus());
meetingManageHelper.buildMeetingQuery(query, po);
Page<Meeting> page = meetingService.page(po.page(), query);
if (page.getTotal() == 0) {
return PageVo.empty();
}
PageVo<MeetingByManagerVO> result = new PageVo<>(new ArrayList<>(), page.getTotal());
Map<Long, CountConfirmByMeetingIdDTO> countConfirmMap = new HashMap<>(16);
List<Long> meetingIds = CollUtils.fieldList(page.getRecords(), Meeting::getId);
countConfirmMap.putAll(meetingExpertService.countConfirmedByMeetingIds(meetingIds));
page.getRecords().forEach(meeting -> {
MeetingByManagerVO item = meetingManageHelper.buildByMeeting(meeting);
CountConfirmByMeetingIdDTO confirm = countConfirmMap.get(meeting.getId());
if (confirm == null) {
item.setInviteCount(0);
item.setConfirmCount(0);
} else {
item.setInviteCount(confirm.getTotal());
item.setConfirmCount(confirm.getConfirmed());
}
result.getRecords().add(item);
});
return result;
}

public MeetingDetailBasicVO getMeetingBasicInfo(Long meetingId) {
Meeting meeting = meetingService.getById(meetingId);
if (Objects.isNull(meeting)) {
throw new BizException("该会议信息不存在");
}
String attachment = meeting.getAttachment();
List<AttachFileVo> attachments = new ArrayList<>();
if (StrUtils.isNotBlank(attachment)) {
attachments.addAll(fileService.getByIds(BizUtils.splitToLong(attachment)));
}
Integer attendStatus = null;
if (LoginUserUtil.isExpert()) {
List<Long> meIds = Collections.singletonList(meetingId);
List<MeetingAndAttendStatusDTO> status = meetingExpertService.listByExpertIdAndStatus(LoginUserUtil.getUserId(), null, meIds);
if (status.isEmpty()) {
throw BizException.wrap("您未被邀请参加次会议");
}
attendStatus = meetingManageHelper.getExpertAttendStatus(status.get(0));
}
return MeetingDetailBasicVO.builder()
.id(meeting.getId())
.name(meeting.getName())
.type(meeting.getType())
.typeName(dictionaryCache.getByCode(meeting.getType()).getName())
.author(meeting.getAuthor())
.startTime(meeting.getStartTime())
.endTime(meeting.getEndTime())
.createOn(meeting.getCreateOn())
.contact(meeting.getContact())
.connecter(meeting.getConnecter())
.description(meeting.getDescription())
.remark(meeting.getRemark())
.address(meeting.getRegionDetail())
.attachments(attachments)
.status(meeting.getStatus())
.attendStatus(attendStatus)
.cancelRemark(meeting.getCancelRemark())
.createBy(meeting.getAuthor())
.inviterCompany(meeting.getHoldCompany())
.invited(meeting.getInvited())
.sendMeetingNotice(meeting.getSendMeetingNotice())
.build();
}

public void uploadMeetingResult(MeetingResultReq po) {
LambdaUpdateWrapper<Meeting> updater = Wrappers.lambdaUpdate(Meeting.class)
.eq(Meeting::getId, po.getMeetingId())
.set(Meeting::getResultDescription, po.getResultDescription())
.set(Meeting::getStatus, Manager.COMPLETED.getCode());
if (CollectionUtils.isNotEmpty(po.getAttachments())) {
updater.set(Meeting::getResultAttachment, CollUtils.joinByComma(po.getAttachments()));
}
meetingService.update(updater);
}

public MeetingResultVO meetingResultDetail(Long meetingId) {
LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
.select(Meeting::getResultAttachment, Meeting::getResultDescription)
.eq(Meeting::getId, meetingId);
Meeting meeting = meetingService.getOne(query);
MeetingResultVO result = new MeetingResultVO();
result.setMeetingId(meetingId);
result.setResultDescription(meeting.getResultDescription());
result.setAttachments(new ArrayList<>());
if (StrUtils.isNotBlank(meeting.getResultAttachment())) {
List<Long> fileIds = BizUtils.splitToLong(meeting.getResultAttachment());
result.getAttachments().addAll(fileService.getByIds(fileIds));
}
return result;
}


public ExpertInviteDetailVO inviteDetail(Long meetingId) {
ExpertInviteDetailVO result = new ExpertInviteDetailVO();
Meeting meeting = meetingService.getById(meetingId);
if (Objects.isNull(meeting)) {
throw new BizException("该会议信息不存在");
}
result.setHasStopInvite(meeting.getStopRandomInvite());
result.setHasSendNotice(meeting.getSendMeetingNotice());
List<MeetingExpert> experts = meetingExpertService.listByMeetingId(meetingId);
if (experts.isEmpty()) {
return result;
}
List<MeetingExpert> randomList = new ArrayList<>();
List<MeetingExpert> appointList = new ArrayList<>();
List<MeetingExpert> attendList = new ArrayList<>();
List<Long> expertIds = new ArrayList<>();
experts.forEach(w -> {
boolean randomInvite = w.getInviteType().equals(ExpertInviteType.RANDOM.getCode());
if (randomInvite) {
randomList.add(w);
} else {
appointList.add(w);
}
if (w.getStatus().equals(ExpertAttendStatus.AGREED.getCode())) {
attendList.add(w);
if (randomInvite) {
result.setRandomAttend(result.getRandomAttend() + 1);
} else {
result.setAppointAttend(result.getAppointAttend() + 1);
}
}
expertIds.add(w.getExpertId());
});
result.setAttendTotal(attendList.size());
Map<Long, ExpertBasicInfoVO> expertBasicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds);
Function<MeetingExpert, RandomInviteListItemVO> mapping = sme -> {
ExpertBasicInfoVO basicInfoVo = expertBasicInfoVoMap.get(sme.getExpertId());
RandomInviteListItemVO item = BeanUtil.copyProperties(basicInfoVo, RandomInviteListItemVO.class);
item.setStatus(sme.getStatus());
item.setMeetingId(sme.getMeetingId());
item.setExpertMeetingId(sme.getId());
ExpertAttendStatus status = ExpertAttendStatus.getByCode(sme.getStatus());
if (status.equals(ExpertAttendStatus.NOTICING)) {
item.setNoticeStatus(status.getDesc());
item.setConfirmResult(StrUtil.EMPTY);
} else {
item.setNoticeStatus("已通知");
item.setConfirmResult(status.getDesc());
}
return item;
};
// 随机邀请列表
randomList.forEach(w -> result.addRandomInviteList(mapping.apply(w)));
// 指定抽取列表
appointList.forEach(w -> result.addAppointInviteList(mapping.apply(w)));
// 确定参加列表
attendList.forEach(w -> {
ExpertBasicInfoVO expertBasicInfoVo = expertBasicInfoVoMap.get(w.getExpertId());
ExpertAttendListItemVO item = BeanUtil.copyProperties(expertBasicInfoVo, ExpertAttendListItemVO.class);
item.setInviteType(ExpertInviteType.getByCode(w.getInviteType()).getName());
result.addAttendList(item);
});
return result;
}

private boolean meetingInfoChange(Meeting old, Meeting current) {
if (!old.getStartTime().equals(current.getStartTime())) {
return Boolean.TRUE;
}
if (!old.getRegionCode().equals(current.getRegionCode())) {
return Boolean.TRUE;
}
if (!old.getRegionDetail().equals(current.getRegionDetail())) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}

/**
* 会议基本信息修改
*
* @param po 修改参数
**/
public void meetingBasicInfoModify(MeetingBasicInfoModifyReq po) {
Meeting meeting = new Meeting();
BeanUtil.copyProperties(po, meeting);
if (CollectionUtils.isEmpty(po.getAttachmentIds())) {
meeting.setAttachment(StrUtil.EMPTY);
} else {
meeting.setAttachment(CollUtils.joinByComma(po.getAttachmentIds()));
}
if (StrUtil.isBlank(po.getRemark())) {
meeting.setRemark(StrUtil.EMPTY);
}
LocalDateTime now = LocalDateTime.now();
meeting.setHoldCompany(po.getInviterCompany());
Meeting old = meetingService.getById(po.getId());
if (Manager.UNCOMPLETED.eq(old.getStatus()) && meetingInfoChange(old, meeting)) {
List<MeetingExpert> meList = meetingExpertService.listExpertByAgreeAttend(Collections.singletonList(po.getId()));
if (!meList.isEmpty() && old.getStartTime().isAfter(now)) {
// TODO
/*String meetingType = dictionaryCache.getByCode(old.getType()).getName();
List<SendSmsContext> contexts = YxtSmsContextBuilder.smsToExpertByMeetingChange(old, meeting, meList, meetingType);
yxtCallOrSmsHelper.sendSms(contexts);*/
}
}
meetingService.updateById(meeting);
}

/**
* 抽取规则详情
*
* @param meetingId 会议ID
* @return {@link InviteRuleDetailVO}
* @author WendyYang
**/
public InviteRuleDetailVO inviteRuleDetail(Long meetingId) {
InviteRuleDetailVO result = new InviteRuleDetailVO();
List<ExpertInviteRule> inviteRules = inviteRuleService.listByMeetingId(meetingId);
if (!result.setAndGetInvited(inviteRules.size() > 0)) {
return result;
}
result.setRandomRules(new ArrayList<>());
Map<ExpertInviteType, List<ExpertInviteRule>> groupByType = CollUtils.group(inviteRules, w -> ExpertInviteType.getByCode(w.getInviteType()));
List<ExpertInviteRule> randoms = groupByType.get(ExpertInviteType.RANDOM);
if (CollectionUtils.isNotEmpty(randoms)) {
randoms.forEach(random -> {
RandomInviteRuleVO randomRule = JSON.parseObject(random.getInviteRule(), RandomInviteRuleVO.class);
if (randomRule.getExpertTags() != null) {
randomRule.getExpertTags().forEach(w -> {
List<String> tagNames = CollUtils.convert(w.getTagCodes(), tagCode -> tagCache.getByTagCode(tagCode).getTagName());
w.setTagCodes(tagNames);
});
}
if (randomRule.getExpertDicts() != null) {
randomRule.getExpertDicts().forEach(w -> {
List<String> dictNames = CollUtils.convert(w.getDictCodes(), dictCode -> dictionaryCache.getByCode(dictCode).getName());
w.setDictCodes(dictNames);
});
}
if (StrUtil.isNotEmpty(randomRule.getIntentionRegionCode())) {
/*List<RegionDTO> intentionRegions = regionCache.listParents(randomRule.getIntentionRegionCode(), randomRule.getIntentionRegionLevel());
randomRule.setIntentionRegions(intentionRegions);*/
}
if (StrUtil.isNotEmpty(randomRule.getExpertRegionCode())) {
/*List<RegionDTO> expertRegions = regionCache.listParents(randomRule.getExpertRegionCode(), randomRule.getExpertRegionLevel());
randomRule.setExpertRegions(expertRegions);*/
}
result.getRandomRules().add(randomRule);
});
}
List<ExpertInviteRule> appoints = groupByType.get(ExpertInviteType.APPOINT);
if (result.setAndGetHasAppointRule(CollUtil.isNotEmpty(appoints))) {
ExpertInviteRule appoint = appoints.get(0);
AppointInviteRuleDTO appointRule = JSON.parseObject(appoint.getInviteRule(), AppointInviteRuleDTO.class);
InviteRuleDetailVO.AppointRuleVo vo = new InviteRuleDetailVO.AppointRuleVo();
vo.setInviteDesc(appointRule.getInviteDesc());
vo.setExperts(new ArrayList<>(meetingManageHelper.getExpertBasicInfo(appointRule.getExpertIds()).values()));
result.setAppointRule(vo);
}
AvoidInfoDTO avoidInfo = inviteAvoidRuleService.getAvoidInfoDto(meetingId);
if (result.setAndGetHasAvoidInfo(avoidInfo != null)) {
InviteRuleDetailVO.AvoidInfoVo vo = new InviteRuleDetailVO.AvoidInfoVo();
vo.setAvoidMates(avoidInfo.getAvoidMates());
vo.setCompanyIds(avoidInfo.getCompanyIds());
if (CollectionUtils.isNotEmpty(avoidInfo.getExpertIds())) {
vo.setExperts(new ArrayList<>(meetingManageHelper.getExpertBasicInfo(avoidInfo.getExpertIds()).values()));
}
result.setAvoidInfo(vo);
}
return result;
}

public void expertRemove(ExpertRemoveReq po) {
LambdaUpdateWrapper<MeetingExpert> update = Wrappers.lambdaUpdate(MeetingExpert.class)
.eq(MeetingExpert::getId, po.getExpertMeetingId())
.set(MeetingExpert::getStatus, ExpertAttendStatus.CANCELED.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(ExpertInviteType.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, ExpertAttendStatus.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(ExpertAttendStatus.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 po) {
List<ExpertUserFullInfo> userInfos = meetingManageHelper.appointExpertCheck(po.getMeetingId(), po.getExpertIds());
List<MeetingExpert> expertList = CollUtils.convert(userInfos, w -> {
MeetingExpert me = ExpertInviteBuilder.getExpertByAppoint(po.getMeetingId(), w, 0L);
me.setStatus(ExpertAttendStatus.NOTICING.getCode());
return me;
});
meetingExpertService.saveBatch(expertList);
}

public void sendMeetingNotice(Long meetingId) {
String key = "SEND_MEETING_NOTICE:" + meetingId;
if (!distributedLock.lock(key, RETRY_TIMES)) {
throw BizException.wrap("正在下发会议通知");
}
try {
Meeting meeting = meetingService.getById(meetingId);
Assert.isTrue(Manager.UNCOMPLETED.eq(meeting.getStatus()), "非未完成会议无法发送会议通知");
Assert.isTrue(!meeting.getSendMeetingNotice(), "该会议已下发过会议通知");
Assert.isTrue(meeting.getStopRandomInvite(), "随机邀请未结束");
int noticeCount = meetingExpertService.countExpertByStatusAndMeetingId(ExpertAttendStatus.NOTICING, meetingId, null);
Assert.isTrue(noticeCount == 0, "存在未确认完成的专家,暂无法下发会议通知");
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class)
.set(Meeting::getSendMeetingNotice, Boolean.TRUE)
.eq(Meeting::getId, meetingId);
meetingService.update(update);
// 发送会议通知
List<MeetingExpert> experts = meetingExpertService.listExpertByAgreeAttend(Collections.singletonList(meetingId));
if (!experts.isEmpty()) {
// TODO
// String meetingType = dictionaryCache.getByCode(meeting.getType()).getName();
// List<SendSmsContext> contexts = YxtSmsContextBuilder.smsToExpertBySendNotice(meeting, experts, meetingType);
// yxtCallOrSmsHelper.sendSms(contexts);
}
} finally {
distributedLock.releaseLock(key);
}
}

public void stopRandomInvite(Long meetingId) {
expertInviteTask.cancelByMeetingId(meetingId);
}

@Transactional(rollbackFor = Exception.class)
public void cancelMeeting(MeetingCancelReq po) {
String key = "CANCEL_MEETING:" + po.getMeetingId();
if (!distributedLock.lock(key, RETRY_TIMES)) {
throw BizException.wrap("正在取消会议,请刷新后重试");
}
try {
Meeting meeting = meetingService.getById(po.getMeetingId());
Assert.isTrue(!Manager.CANCELED.eq(meeting.getStatus()), "会议已取消");
Assert.isTrue(meeting.getStartTime().isAfter(LocalDateTime.now()), "会议已开始,暂时无法取消");
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class)
.set(Meeting::getStatus, Manager.CANCELED.getCode())
.set(Meeting::getUpdateBy, LoginUserUtil.getUserId())
.set(Meeting::getCancelRemark, po.getCancelRemark())
.set(Meeting::getUpdateOn, LocalDateTime.now())
.eq(Meeting::getId, po.getMeetingId());
meetingService.update(update);
expertInviteTask.cancelByMeetingId(po.getMeetingId());
// 发送通知给专家
List<MeetingExpert> experts = meetingExpertService.listExpertByAgreeAttend(Collections.singletonList(po.getMeetingId()));
if (!experts.isEmpty()) {
// TODO
// meeting.setCancelRemark(po.getCancelRemark());
// String meetingType = dictionaryCache.getByCode(meeting.getType()).getName();
// List<SendSmsContext> contexts = YxtSmsContextBuilder.smsToExpertByCancelMeeting(meeting, experts, meetingType);
// yxtCallOrSmsHelper.sendSms(contexts);
}
} finally {
distributedLock.releaseLock(key);
}
}

public ExpertInvitationDetailVO expertInvitationDetail(Long meetingId, Long expertId) {
Long userId = expertId == null ? LoginUserUtil.getUserId() : expertId;
MeetingExpert me = meetingExpertService.getByMeetingIdAndExpertId(meetingId, userId);
Assert.notNull(me, "未被邀请参加");
Assert.isTrue(ExpertAttendStatus.AGREED.eq(me.getStatus()), "未确认参加");
ExpertUserFullInfo expertInfo = expertUserFullInfoService.getByUserId(userId);
Meeting meeting = meetingService.getById(meetingId);
return ExpertInvitationDetailVO.builder()
.expertName(expertInfo.getExpertName())
.holdCompany(meeting.getHoldCompany())
.regionDetail(meeting.getRegionDetail())
.meetingName(meeting.getName())
.startTime(meeting.getStartTime())
.endTime(meeting.getEndTime())
.inviteTime(me.getCreateOn())
.connecter(meeting.getConnecter())
.contact(meeting.getContact())
.build();
}

public void confirmAttendByManager(ExpertRemoveReq po) {
MeetingExpert meetingExpert = meetingExpertService.getById(po.getExpertMeetingId());
if (meetingExpert.getStatus().equals(ExpertAttendStatus.NOTICING.getCode())) {
LambdaUpdateWrapper<MeetingExpert> update = Wrappers.lambdaUpdate(MeetingExpert.class)
.set(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode())
.set(MeetingExpert::getUpdateOn, LocalDateTime.now())
.set(MeetingExpert::getUpdateBy, LoginUserUtil.getUserId())
.eq(MeetingExpert::getId, po.getExpertMeetingId());
meetingExpertService.update(update);
} else {
ExpertAttendStatus status = ExpertAttendStatus.getByCode(meetingExpert.getStatus());
throw BizException.wrap("该专家" + status.getDesc());
}
}

}

+ 16
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.java 파일 보기

@@ -0,0 +1,16 @@
package com.ningdatech.pmapi.meeting.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule;

/**
* <p>
* Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
public interface ExpertInviteAvoidRuleMapper extends BaseMapper<ExpertInviteAvoidRule> {

}

+ 5
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.xml 파일 보기

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.meeting.mapper.ExpertInviteAvoidRuleMapper">

</mapper>

+ 16
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.java 파일 보기

@@ -0,0 +1,16 @@
package com.ningdatech.pmapi.meeting.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;

/**
* <p>
* Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
public interface ExpertInviteRuleMapper extends BaseMapper<ExpertInviteRule> {

}

+ 5
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.xml 파일 보기

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.meeting.mapper.ExpertInviteRuleMapper">

</mapper>

+ 26
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.java 파일 보기

@@ -0,0 +1,26 @@
package com.ningdatech.pmapi.meeting.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpertEvaluation;
import org.apache.ibatis.annotations.Param;

import java.util.Collection;

/**
* <p>
* 专家评价表 Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-07-25
*/
public interface MeetingExpertEvaluationMapper extends BaseMapper<MeetingExpertEvaluation> {

Page<CountGroupByDTO<Long>> countExpertAttend(@Param("expertIds") Collection<Long> expertIds, @Param("isAttended") Boolean isAttended, Page<CountGroupByDTO<Long>> page);

Page<MeetingExpert> pageExpertEvaluationToDo(@Param("meetingIds") Collection<Long> meetingIds, Page<MeetingExpert> po);

}

+ 25
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.xml 파일 보기

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.meeting.mapper.MeetingExpertEvaluationMapper">

<select id="countExpertAttend" resultType="com.ningdatech.pmapi.common.model.entity.CountGroupByDTO">
select count(1) total, expert_id groupKey from meeting_expert_evaluation
where expert_id
<foreach collection="expertIds" separator="," close=")" open=" in (" item="expertId">#{expertId}</foreach>
<if test="isAttended != null">
and is_attended = #{isAttended}
</if>
group by expert_id order by total desc
</select>

<select id="pageExpertEvaluationToDo"
resultType="com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert">
select * from (SELECT ROW_NUMBER() OVER ( PARTITION BY expert_id ORDER BY update_on DESC )
rowNumber, ID, expert_id, status, meeting_id FROM meeting_expert
where meeting_id <foreach collection="meetingIds" item="item" open=" in (" close=")" separator=",">
#{item}</foreach>) em
WHERE rowNumber = 1 and status = 3 and not exists (select 1 from meeting_expert_evaluation where
expert_meeting_id = em.id)
</select>

</mapper>

+ 81
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.java 파일 보기

@@ -0,0 +1,81 @@
package com.ningdatech.pmapi.meeting.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
import org.apache.ibatis.annotations.Param;

import java.util.Collection;
import java.util.List;

/**
* <p>
* 事务专家表 Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-07-27
*/
public interface MeetingExpertMapper extends BaseMapper<MeetingExpert> {

/**
* 根据专家状态查询需要参加的会议信息
*
* @param expertId 专家ID
* @param status 状态
* @param meetingIds 会议ID
* @return 专家参会状态列表
* @author WendyYang
**/
List<MeetingAndAttendStatusDTO> selectByExpertIdAndStatus(@Param("expertId") Long expertId,
@Param("status") Integer status,
@Param("meetingIds") List<Long> meetingIds);

/**
* 分页查询专家列表
*
* @param page 分页数据
* @param status 状态{@link ExpertAttendStatus}
* @param meetingId 会议ID
* @param inviteType 邀请类型
* @return Page<MeetingExpert>
* @author WendyYang
**/
Page<MeetingExpert> selectExpertByStatusAndMeetingId(Page<MeetingExpert> page, @Param("status") Integer status,
@Param("meetingId") Long meetingId,
@Param("inviteType") Integer inviteType);

/**
* 分页查询专家列表
*
* @param page 分页数据
* @param status 状态{@link ExpertAttendStatus}
* @param meetingIds 会议ID
* @return Page<MeetingExpert>
* @author WendyYang
**/
Page<MeetingExpert> selectExpertByStatusAndMeetingIds(Page<MeetingExpert> page, @Param("status") Integer status,
@Param("meetingIds") Collection<Long> meetingIds);

/**
* 根据会议ID与参与状态统计专家数量
*
* @param status 会议状态
* @param meetingId 会议ID
* @param inviteType 邀请类型
* @return 数量
*/
int countExpertByStatusAndMeetingId(@Param("status") Integer status, @Param("meetingId") Long meetingId, @Param("inviteType") Integer inviteType);

/**
* 查询会议的所有被抽取人最后一条记录
*
* @param meetingIds 会议ID
* @return 抽取记录
* @author WendyYang
**/
List<MeetingExpert> listExpertLastByMeetingIds(@Param("meetingIds") Collection<Long> meetingIds);

}

+ 82
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.xml 파일 보기

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.meeting.mapper.MeetingExpertMapper">

<select id="selectByExpertIdAndStatus"
resultType="com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO">
SELECT em.meeting_id meetingId, em.status, mee.is_attended attended
FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY meeting_id ORDER BY update_on DESC ) rowNumber, ID, expert_id,
status, meeting_id
FROM meeting_expert
where expert_id = #{expertId}
<if test="meetingIds != null and meetingIds.size > 0">
and meeting_id
<foreach collection="meetingIds" open="in (" close=")" item="item">#{item}</foreach>
</if>
) em
left join meeting_expert_evaluation mee on mee.expert_meeting_id = em.id
WHERE rowNumber = 1
<if test="status == 1">
AND em.status = 3 and mee.is_attended is null
</if>
<if test="status == 2">
AND em.status = 3 and mee.is_attended = true
</if>
<if test="status == 3">
AND em.status = 6
</if>
<if test="status == null">
and em.status in (3,6)
</if>
</select>

<select id="selectExpertByStatusAndMeetingId"
resultType="com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert">
SELECT * FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY expert_id ORDER BY update_on DESC ) rowNumber,
ID, expert_id, status, meeting_id, invite_type, mobile, expert_name, update_on FROM meeting_expert
where meeting_id = #{meetingId}
<if test="inviteType != null">
and invite_type = #{inviteType}
</if>
) em
WHERE rowNumber = 1
<if test="status != null">
and status = #{status}
</if>
</select>

<select id="selectExpertByStatusAndMeetingIds"
resultType="com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert">
SELECT * FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY expert_id, meeting_id ORDER BY update_on DESC ) rowNumber,
ID, expert_id, status, meeting_id, invite_type, mobile, expert_name, update_on FROM meeting_expert
where meeting_id <foreach collection="meetingIds" separator="," close=")" open=" in (" item="item">
#{item}</foreach>) em
WHERE rowNumber = 1
<if test="status != null">
and status = #{status}
</if>
</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 )
rowNumber, status FROM meeting_expert
where meeting_id = #{meetingId}
<if test="inviteType != null">
and invite_type = #{inviteType}
</if>
) em
WHERE rowNumber = 1
<if test="status != null">
and status = #{status}
</if>
</select>

<select id="listExpertLastByMeetingIds" resultType="com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert">
SELECT * FROM (SELECT ROW_NUMBER() OVER ( PARTITION BY expert_id, meeting_id ORDER BY update_on DESC ) rowNumber,
ID, expert_id, status, meeting_id, invite_type, mobile, expert_name, update_on, pre_id, rule_id FROM meeting_expert
where meeting_id <foreach collection="meetingIds" separator="," close=")" open=" in (" item="item">
#{item}</foreach>) em WHERE rowNumber = 1
</select>

</mapper>

+ 21
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.java 파일 보기

@@ -0,0 +1,21 @@
package com.ningdatech.pmapi.meeting.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;

import java.util.List;

/**
* <p>
* 会议 Mapper 接口
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
public interface MeetingMapper extends BaseMapper<Meeting> {

List<CountGroupByDTO<String>> meetingCountSummary(Long createBy);

}

+ 18
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.xml 파일 보기

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ningdatech.pmapi.meeting.mapper.MeetingMapper">

<select id="meetingCountSummary" resultType="com.ningdatech.pmapi.common.model.entity.CountGroupByDTO">
select status groupKey, count(1) total
from (SELECT (CASE
WHEN (status = 1 AND now() &lt; start_time) THEN 'TO_BE_HELD'
WHEN (status = 1 AND now() &gt;= start_time) THEN 'HELD'
WHEN status = 2 THEN 'COMPLETED'
ELSE 'CANCELED'
END) status
FROM meeting
where create_by = #{createBy}) temp
GROUP BY status
</select>

</mapper>

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java 파일 보기

@@ -0,0 +1,28 @@
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;

/**
* <p>
* IExpertInviteAvoidRuleService服务类
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
public interface IExpertInviteAvoidRuleService extends IService<ExpertInviteAvoidRule> {

ExpertInviteAvoidRule getByMeetingId(Long meetingId);

/**
* 获取回避信息
*
* @param meetingId 会议ID
* @return com.ningdatech.emapi.meeting.entity.dto.AvoidInfoDto
* @author WendyYang
**/
AvoidInfoDTO getAvoidInfoDto(Long meetingId);

}

+ 24
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteRuleService.java 파일 보기

@@ -0,0 +1,24 @@
package com.ningdatech.pmapi.meeting.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;

import java.util.List;
import java.util.Map;

/**
* <p>
* 服务类
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
public interface IExpertInviteRuleService extends IService<ExpertInviteRule> {

List<ExpertInviteRule> listByMeetingId(Long meetingId);

Map<Long, RandomInviteRuleDTO> randomRuleByMeetingId(Long meetingId);

}

+ 26
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertEvaluationService.java 파일 보기

@@ -0,0 +1,26 @@
package com.ningdatech.pmapi.meeting.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.basic.model.PagePo;
import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpertEvaluation;

import java.util.Collection;

/**
* <p>
* 专家评价表 服务类
* </p>
*
* @author WendyYang
* @since 2022-07-25
*/
public interface IMeetingExpertEvaluationService extends IService<MeetingExpertEvaluation> {

Page<CountGroupByDTO<Long>> listExpertAttendSummary(Collection<Long> expertIds, Boolean isAttended, Page<CountGroupByDTO<Long>> page);

Page<MeetingExpert> pageExpertEvaluationTodo(Collection<Long> meetingIds, PagePo po);

}

+ 123
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java 파일 보기

@@ -0,0 +1,123 @@
package com.ningdatech.pmapi.meeting.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;

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

/**
* <p>
* 事务专家表 服务类
* </p>
*
* @author WendyYang
* @since 2022-07-27
*/
public interface IMeetingExpertService extends IService<MeetingExpert> {

/**
* 查询专家的参与状态
*
* @param expertId 专家ID
* @param status 状态{@link MeetingStatus.Expert}
* @param meetingIds 会议ID
* @return 会议参加状态统计
* @author WendyYang
**/
List<MeetingAndAttendStatusDTO> listByExpertIdAndStatus(Long expertId, Integer status, List<Long> meetingIds);

/**
* 查询每个事物的确认进度
*
* @param meetingIds 事务ID
* @return 确认进度
* @author WendyYang
**/
Map<Long, CountConfirmByMeetingIdDTO> countConfirmedByMeetingIds(List<Long> meetingIds);

/**
* 根据事务ID查询所有邀请记录
*
* @param meetingId 事务ID
* @return 邀请专家记录
*/
List<MeetingExpert> listByMeetingId(Long meetingId);

/**
* 根据事务ID查询所有邀请记录
*
* @param meetingIds 事务ID
* @return List<SubjectMatterExpert>
*/
List<MeetingExpert> listByMeetingIds(List<Long> meetingIds);

/**
* 获取专家参与事务的最新记录
*
* @param meetingId 会议ID
* @param expertId 专家ID
* @return 专家参会记录
* @author WendyYang
**/
MeetingExpert getByMeetingIdAndExpertId(Long meetingId, Long expertId);

/**
* 根据状态与会议ID分页查询专家列表
*
* @param page 分页
* @param meetingId 会议ID
* @param status 状态
* @param inviteType 邀请类型
* @return Page<MeetingExpert>
* @author WendyYang
**/
Page<MeetingExpert> pageExpertByStatusAndMeetingId(Page<MeetingExpert> page, Long meetingId, ExpertAttendStatus status, Integer inviteType);

/**
* 批量查询某个状态的专家邀请记录
*
* @param page 分页参数
* @param meetingIds 会议ID
* @param status 状态
* @return 专家邀请记录
* @author WendyYang
**/
Page<MeetingExpert> pageExpertByStatusAndMeetingIds(Page<MeetingExpert> page, List<Long> meetingIds, ExpertAttendStatus status);

/**
* 根据邀请类型统计会议下某个状态的专家数量
*
* @param status 状态
* @param meetingId 会议ID
* @param inviteType 邀请类型
* @return int
* @author WendyYang
**/
int countExpertByStatusAndMeetingId(ExpertAttendStatus status, Long meetingId, ExpertInviteType inviteType);

/**
* 查询所有同意参加的专家记录
*
* @param meetingIds 会议ID
* @return List<MeetingExpert>
* @author WendyYang
**/
List<MeetingExpert> listExpertByAgreeAttend(Collection<Long> meetingIds);

/**
* 查询会议的所有被抽取人最后一条记录
*
* @param meetingIds 会议ID
* @return 抽取记录
* @author WendyYang
**/
List<MeetingExpert> listExpertLastByMeetingIds(Collection<Long> meetingIds);

}

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingService.java 파일 보기

@@ -0,0 +1,28 @@
package com.ningdatech.pmapi.meeting.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard;

import java.util.Map;

/**
* <p>
* 会议 服务类
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
public interface IMeetingService extends IService<Meeting> {

/**
* 停止抽取
*
* @param meetingId 会议ID
**/
void stopRandomInvite(Long meetingId);

Map<MeetingStatusByDashboard, Integer> meetingCountSummary(Long createBy);

}

+ 45
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java 파일 보기

@@ -0,0 +1,45 @@
package com.ningdatech.pmapi.meeting.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
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.mapper.ExpertInviteAvoidRuleMapper;
import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
import org.springframework.stereotype.Service;

/**
* <p>
* 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Service
public class ExpertInviteAvoidRuleServiceImpl extends ServiceImpl<ExpertInviteAvoidRuleMapper, ExpertInviteAvoidRule> implements IExpertInviteAvoidRuleService {

@Override
public ExpertInviteAvoidRule getByMeetingId(Long meetingId) {
LambdaQueryWrapper<ExpertInviteAvoidRule> query = Wrappers.lambdaQuery(ExpertInviteAvoidRule.class)
.eq(ExpertInviteAvoidRule::getMeetingId, meetingId);
return getOne(query);
}

@Override
public AvoidInfoDTO getAvoidInfoDto(Long meetingId) {
ExpertInviteAvoidRule avoidRule = getByMeetingId(meetingId);
if (avoidRule == null) {
return null;
}
AvoidInfoDTO avoidInfo = new AvoidInfoDTO();
avoidInfo.setAvoidMates(avoidRule.getAvoidMates());
avoidInfo.setExpertIds(BizUtils.splitToLong(avoidRule.getExpertIds()));
avoidInfo.setCompanyIds(StrUtils.split(avoidRule.getCompanyIds()));
return avoidInfo;
}

}

+ 47
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteRuleServiceImpl.java 파일 보기

@@ -0,0 +1,47 @@
package com.ningdatech.pmapi.meeting.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
import com.ningdatech.pmapi.meeting.mapper.ExpertInviteRuleMapper;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* <p>
* 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Service
public class ExpertInviteRuleServiceImpl extends ServiceImpl<ExpertInviteRuleMapper, ExpertInviteRule> implements IExpertInviteRuleService {

public static LambdaQueryWrapper<ExpertInviteRule> wrapperByMeetingId(Long meetingId) {
return Wrappers.lambdaQuery(ExpertInviteRule.class).eq(ExpertInviteRule::getMeetingId, meetingId);
}

@Override
public List<ExpertInviteRule> listByMeetingId(Long meetingId) {
return list(wrapperByMeetingId(meetingId));
}

@Override
public Map<Long, RandomInviteRuleDTO> randomRuleByMeetingId(Long meetingId) {
LambdaQueryWrapper<ExpertInviteRule> query = wrapperByMeetingId(meetingId)
.eq(ExpertInviteRule::getInviteType, ExpertInviteType.RANDOM.getCode());
List<ExpertInviteRule> inviteRules = baseMapper.selectList(query);
return inviteRules.stream().collect(Collectors.toMap(ExpertInviteRule::getId,
w -> JSONObject.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class)));
}

}

+ 36
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertEvaluationServiceImpl.java 파일 보기

@@ -0,0 +1,36 @@
package com.ningdatech.pmapi.meeting.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.model.PagePo;
import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpertEvaluation;
import com.ningdatech.pmapi.meeting.mapper.MeetingExpertEvaluationMapper;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService;
import org.springframework.stereotype.Service;

import java.util.Collection;

/**
* <p>
* 专家评价表 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-07-25
*/
@Service
public class MeetingExpertEvaluationServiceImpl extends ServiceImpl<MeetingExpertEvaluationMapper, MeetingExpertEvaluation> implements IMeetingExpertEvaluationService {

@Override
public Page<CountGroupByDTO<Long>> listExpertAttendSummary(Collection<Long> expertIds, Boolean isAttended, Page<CountGroupByDTO<Long>> page) {
return baseMapper.countExpertAttend(expertIds, isAttended, page);
}

@Override
public Page<MeetingExpert> pageExpertEvaluationTodo(Collection<Long> meetingIds, PagePo po) {
return baseMapper.pageExpertEvaluationToDo(meetingIds, new Page<>(po.getPageNumber(), po.getPageSize()));
}

}

+ 135
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertServiceImpl.java 파일 보기

@@ -0,0 +1,135 @@
package com.ningdatech.pmapi.meeting.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
import com.ningdatech.pmapi.meeting.mapper.ExpertInviteRuleMapper;
import com.ningdatech.pmapi.meeting.mapper.MeetingExpertMapper;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* <p>
* 事务专家表 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-07-27
*/
@Service
@RequiredArgsConstructor
public class MeetingExpertServiceImpl extends ServiceImpl<MeetingExpertMapper, MeetingExpert> implements IMeetingExpertService {

private final ExpertInviteRuleMapper inviteRuleMapper;

@Override
public List<MeetingAndAttendStatusDTO> listByExpertIdAndStatus(Long expertId, Integer status, List<Long> meetingIds) {
return baseMapper.selectByExpertIdAndStatus(expertId, status, meetingIds);
}

@Override
public Map<Long, CountConfirmByMeetingIdDTO> countConfirmedByMeetingIds(List<Long> meetingIds) {
List<ExpertInviteRule> inviteRules = inviteRuleMapper.selectList(Wrappers.lambdaQuery(ExpertInviteRule.class)
.eq(ExpertInviteRule::getInviteType, ExpertInviteType.RANDOM.getCode())
.in(ExpertInviteRule::getMeetingId, meetingIds));
Map<Long, Integer> countByRandom = CollUtils.groupSumInt(inviteRules, ExpertInviteRule::getMeetingId, ExpertInviteRule::getInviteCount);
Page<MeetingExpert> page = pageExpertByStatusAndMeetingIds(new Page<>(0, 500), meetingIds, null);
return page.getRecords().stream().collect(Collectors.groupingBy(MeetingExpert::getMeetingId,
Collectors.collectingAndThen(Collectors.toList(),
w -> {
Long meetingId = w.get(0).getMeetingId();
CountConfirmByMeetingIdDTO confirm = CountConfirmByMeetingIdDTO.builder()
.confirmed(0)
.total(countByRandom.getOrDefault(meetingId, 0))
.meetingId(meetingId)
.build();
w.forEach(item -> {
ExpertAttendStatus attendStatus = ExpertAttendStatus.getByCode(item.getStatus());
if (item.getInviteType().equals(ExpertInviteType.APPOINT.getCode())) {
// 被替换和已取消的不计数
if (attendStatus.equals(ExpertAttendStatus.CANCELED)
|| attendStatus.equals(ExpertAttendStatus.REPLACED)) {
return;
}
confirm.setTotal(confirm.getTotal() + 1);
}
// 除通知中的均为已确认
if (attendStatus.equals(ExpertAttendStatus.AGREED)) {
confirm.setConfirmed(confirm.getConfirmed() + 1);
}
});
return confirm;
})));
}

@Override
public List<MeetingExpert> listByMeetingId(Long meetingId) {
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class)
.eq(MeetingExpert::getMeetingId, meetingId)
.orderByDesc(MeetingExpert::getUpdateOn);
return list(query);
}

@Override
public List<MeetingExpert> listByMeetingIds(List<Long> meetingIds) {
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class)
.in(MeetingExpert::getMeetingId, meetingIds)
.orderByDesc(MeetingExpert::getUpdateOn);
return list(query);
}

@Override
public MeetingExpert getByMeetingIdAndExpertId(Long meetingId, Long expertId) {
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class)
.eq(MeetingExpert::getExpertId, expertId)
.eq(MeetingExpert::getMeetingId, meetingId)
.orderByDesc(MeetingExpert::getUpdateOn)
.last("limit 1");
return getOne(query);
}

@Override
public Page<MeetingExpert> pageExpertByStatusAndMeetingId(Page<MeetingExpert> page, Long meetingId, ExpertAttendStatus status, Integer inviteType) {
return baseMapper.selectExpertByStatusAndMeetingId(page, status.getCode(), meetingId, inviteType);
}

@Override
public Page<MeetingExpert> pageExpertByStatusAndMeetingIds(Page<MeetingExpert> page, List<Long> meetingIds, ExpertAttendStatus status) {
return baseMapper.selectExpertByStatusAndMeetingIds(page, status == null ? null : status.getCode(), meetingIds);
}

@Override
public int countExpertByStatusAndMeetingId(ExpertAttendStatus status, Long meetingId, ExpertInviteType inviteType) {
Integer tempStatus = status == null ? null : status.getCode();
Integer tempInviteType = inviteType == null ? null : inviteType.getCode();
return baseMapper.countExpertByStatusAndMeetingId(tempStatus, meetingId, tempInviteType);
}

@Override
public List<MeetingExpert> listExpertByAgreeAttend(Collection<Long> meetingIds) {
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class)
.eq(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode())
.in(MeetingExpert::getMeetingId, meetingIds);
return baseMapper.selectList(query);
}

@Override
public List<MeetingExpert> listExpertLastByMeetingIds(Collection<Long> meetingIds) {
return baseMapper.listExpertLastByMeetingIds(meetingIds);
}

}

+ 42
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingServiceImpl.java 파일 보기

@@ -0,0 +1,42 @@
package com.ningdatech.pmapi.meeting.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard;
import com.ningdatech.pmapi.meeting.mapper.MeetingMapper;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
* <p>
* 会议 服务实现类
* </p>
*
* @author WendyYang
* @since 2022-07-26
*/
@Service
public class MeetingServiceImpl extends ServiceImpl<MeetingMapper, Meeting> implements IMeetingService {

@Override
public void stopRandomInvite(Long meetingId) {
Meeting meeting = new Meeting();
meeting.setId(meetingId);
meeting.setStopRandomInvite(Boolean.TRUE);
baseMapper.updateById(meeting);
}

@Override
public Map<MeetingStatusByDashboard, Integer> meetingCountSummary(Long createBy) {
List<CountGroupByDTO<String>> meetingCountSummary = baseMapper.meetingCountSummary(createBy);
return CollUtils.listToMap(meetingCountSummary,
w -> MeetingStatusByDashboard.valueOf(w.getGroupKey()),
CountGroupByDTO::getTotal);
}

}

+ 241
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java 파일 보기

@@ -0,0 +1,241 @@
//package com.ningdatech.pmapi.meeting.task;
//
//import com.alibaba.fastjson.JSON;
//import com.alibaba.fastjson.JSONObject;
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
//import com.ningdatech.basic.util.StrPool;
//import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
//import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
//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.RandomInviteRuleDTO;
//import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
//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.DictionaryCache;
//import com.ningdatech.pmapi.sms.utils.DateUtil;
//import lombok.AllArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.PostConstruct;
//import java.time.Duration;
//import java.time.Instant;
//import java.time.LocalDateTime;
//import java.time.temporal.ChronoUnit;
//import java.util.*;
//import java.util.stream.Collectors;
//
//import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus.AGREED;
//import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus.NOTICING;
//
//
///**
// * <p>
// * ExpertCallResultRewriteTask
// * <br>
// * 专家电话结果回填
// * </p>
// *
// * @author WendyYang
// * @since 14:15 2022/8/18
// */
//@Slf4j
//@Component
//@AllArgsConstructor
//public class ExpertCallResultRewriteTask {
//
// private final RandomInviteProperties randomInviteProperties;
//
// private final ThreadPoolTaskScheduler scheduler;
// private final IMeetingExpertService meetingExpertService;
// private final IExpertInviteRuleService inviteRuleService;
// private final IMeetingService meetingService;
// private final ISysMsgRecordDetailService msgRecordDetailService;
// private final DictionaryCache dictionaryCache;
// private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
// private final IExpertUserFullInfoService iExpertUserFullInfoService;
//
// @PostConstruct
// public void initTask() {
// if (!randomInviteProperties.getEnable()) {
// log.warn("随机邀请已关闭……");
// return;
// }
// Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES);
// // 处理电话结果回填
// scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()));
// }
//
//
// public void rewritePhoneCallResult() {
// log.info("开始执行电话结果回填任务:{}", Thread.currentThread().getName());
// // 查询所有邀请的专家信息 状态为通话中的
// LambdaQueryWrapper<MeetingExpert> meQuery = Wrappers.lambdaQuery(MeetingExpert.class)
// .eq(MeetingExpert::getStatus, NOTICING.getCode())
// .eq(MeetingExpert::getInviteType, ExpertInviteType.RANDOM.getCode());
// List<MeetingExpert> experts = meetingExpertService.list(meQuery);
// if (experts.isEmpty()) {
// log.info("暂无电话结果回填任务执行");
// return;
// }
// // 所有随机邀请的规则ID
// Map<Long, String> submitKeys = new HashMap<>(experts.size());
// Set<Long> randomRuleIds = experts.stream().peek(w -> submitKeys.put(w.getId(), w.getSubmitKey()))
// .map(MeetingExpert::getRuleId).collect(Collectors.toSet());
// // 查询随机邀请回调等待时间
// Map<Long, Integer> callbackMinutes = new HashMap<>(randomRuleIds.size());
// if (!randomRuleIds.isEmpty()) {
// List<ExpertInviteRule> inviteRules = inviteRuleService.listByIds(randomRuleIds);
// inviteRules.forEach(w -> {
// RandomInviteRuleDTO rule = JSON.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class);
// callbackMinutes.put(w.getId(), rule.getWaitForCallbackMinutes());
// });
// }
// LambdaQueryWrapper<SysMsgRecordDetail> msgRecordDetailQuery = Wrappers.lambdaQuery(SysMsgRecordDetail.class)
// .in(SysMsgRecordDetail::getSubmitKey, submitKeys.values());
// List<SysMsgRecordDetail> recordDetailList = msgRecordDetailService.list(msgRecordDetailQuery);
// if (recordDetailList.isEmpty()) {
// return;
// }
// Map<String, SysMsgRecordDetail> recordDetailMap = recordDetailList.stream()
// .collect(Collectors.toMap(w -> w.getSubmitKey() + StrPool.UNDERSCORE + w.getReceiveNumber(), w -> w));
// List<MeetingExpert> updates = new ArrayList<>(), agrees = new ArrayList<>();
// for (MeetingExpert expert : experts) {
// String key = expert.getSubmitKey() + StrPool.UNDERSCORE + expert.getMobile();
// SysMsgRecordDetail msgRecordDetail = recordDetailMap.get(key);
// if (msgRecordDetail == null) {
// // 极端情况下获取不到submitKey异常情况
// continue;
// }
// Integer minutes = callbackMinutes.get(expert.getRuleId());
// Optional<Integer> status = getStatusByMsgRecordDetail(msgRecordDetail, minutes, expert.getCreateOn());
// if (status.isPresent()) {
// MeetingExpert update = new MeetingExpert();
// update.setUpdateBy(0L);
// update.setUpdateOn(LocalDateTime.now());
// update.setId(expert.getId());
// update.setPreStatus(expert.getStatus());
// update.setStatus(status.get());
// if (AGREED.eq(update.getStatus())) {
// sendAgreeNotice(expert);
// agrees.add(expert);
// }
// updates.add(update);
// }
// }
// meetingExpertService.updateBatchById(updates);
// if (agrees.size() > 0) {
// obtainCallBackAfterMeetingCanceled(agrees);
// }
// }
//
// private void sendAgreeNotice(MeetingExpert expert) {
// try {
// Meeting meeting = meetingService.getById(expert.getMeetingId());
// if (Objects.isNull(meeting)) {
// return;
// }
// ExpertUserFullInfo expertUserFullInfo = iExpertUserFullInfoService.getByUserId(expert.getExpertId());
// String expertName = null;
// if (Objects.nonNull(expertUserFullInfo)) {
// expertName = expertUserFullInfo.getExpertName();
// }
// String smsContent = String.format(YxtSmsTemplateConst.EXPERT_AGREE_NOTICE, meeting.getHoldCompany(), expertName, meeting.getName()
// , meeting.getStartTime().format(DateUtil.DTF_YMD_HM), meeting.getRegionDetail(), meeting.getConnecter(), meeting.getContact(), WebProperties.webUrl);
// SendSmsContext context = new SendSmsContext();
// context.setContent(smsContent);
// context.setReceiveNumber(expert.getMobile());
//
// yxtCallOrSmsHelper.sendSms(context);
// } catch (Exception e) {
// log.info("发送专家会议接受通知短信失败:{}", JSONObject.toJSONString(expert));
// }
// }
//
// private static Optional<Integer> getStatusByMsgRecordDetail(SysMsgRecordDetail msgRecordDetail, int minutes, LocalDateTime createOn) {
// LocalDateTime time = LocalDateTime.now().minusMinutes(minutes);
// String callBackJson = msgRecordDetail.getCallBackJson();
// if (StrUtils.isBlank(callBackJson) && time.isBefore(createOn)) {
// return Optional.empty();
// }
// ExpertAttendStatus status;
// if (StrUtils.isNotBlank(callBackJson)) {
// try {
// JSONObject callbackObject = JSON.parseObject(callBackJson);
// Date dialBeginTime = callbackObject.getDate("dialBeginTime");
// if (dialBeginTime == null) {
// return Optional.empty();
// }
// Integer resultCode = callbackObject.getInteger("resultCode");
// if (resultCode != null && resultCode == 0) {
// String pressKeyStr = callbackObject.getString("pressKey");
// if (Objects.nonNull(pressKeyStr)) {
// pressKeyStr = pressKeyStr.replaceAll("\\*", "").trim();
// }
// Integer pressKey = null;
// if (StringUtils.isNotBlank(pressKeyStr)) {
// pressKey = Integer.parseInt(pressKeyStr);
// }
// if (pressKey == null) {
// if (time.isBefore(createOn)) {
// return Optional.empty();
// }
// status = REFUSED;
// } else {
// if (pressKey == 1) {
// status = AGREED;
// } else {
// status = REFUSED;
// }
// }
// } else {
// status = NOT_ANSWERED;
// }
// } catch (Exception e) {
// log.error("获取电话回调结果异常", e);
// status = NOT_ANSWERED;
// }
// } else {
// // 超时未回复设置为拒绝参加
// status = REFUSED;
// }
// return Optional.of(status.getCode());
// }
//
// /**
// * 会议取消之后拿到回调结果的话需要发送取消短信
// **/
// private void obtainCallBackAfterMeetingCanceled(List<MeetingExpert> experts) {
// /*List<Long> meetingIds = CollUtils.fieldList(experts, MeetingExpert::getMeetingId);
// LambdaQueryWrapper<Meeting> mQuery = Wrappers.lambdaQuery(Meeting.class)
// .eq(Meeting::getStatus, Manager.CANCELED.getCode())
// .in(Meeting::getId, meetingIds);
// Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetingService.list(mQuery), Meeting::getId);
// if (meetingMap.size() > 0) {
// Map<Meeting, List<MeetingExpert>> expertList = new HashMap<>(16);
// experts.forEach(w -> {
// Meeting meeting = meetingMap.get(w.getMeetingId());
// if (meeting == null) {
// return;
// }
// List<MeetingExpert> list = expertList.computeIfAbsent(meeting, k -> new ArrayList<>());
// list.add(w);
// });
// if (!expertList.isEmpty()) {
// expertList.forEach((m, mes) -> {
// String meetingType = dictionaryCache.getByCode(m.getType()).getName();
// List<SendSmsContext> contexts = YxtSmsContextBuilder.smsToExpertByCancelMeeting(m, mes, meetingType);
// yxtCallOrSmsHelper.sendSms(contexts);
// });
// }
// }*/
// }
//
//}

+ 29
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteExecutorConfig.java 파일 보기

@@ -0,0 +1,29 @@
package com.ningdatech.pmapi.meeting.task;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/**
* <p>
* ExpertInviteExecutorConfig
* </p>
*
* @author WendyYang
* @since 16:21 2022/8/16
*/
@Configuration
public class ExpertInviteExecutorConfig {

@Bean(name = "expertInviteScheduler")
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(3);
scheduler.setThreadGroupName("expert-invite");
scheduler.setThreadNamePrefix("invite-executor-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}

}

+ 326
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java 파일 보기

@@ -0,0 +1,326 @@
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.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.ExpertChooseDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper;
import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
import com.ningdatech.pmapi.meeting.manage.ExpertInviteManage;
import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
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.user.util.LoginUserUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
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;


/**
* <p>
* ExpertInviteTask
* </p>
*
* @author WendyYang
* @since 21:25 2022/8/15
*/
@Slf4j
@Component
@AllArgsConstructor
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 final CachePlusOps cachePlusOps;
@Qualifier("expertInviteScheduler")
@Resource(name = "expertInviteScheduler")
private ThreadPoolTaskScheduler scheduler;
private final IMeetingExpertService meetingExpertService;
private final IExpertInviteRuleService inviteRuleService;
private final IMeetingService meetingService;
private final ExpertInviteManage expertInviteManage;
private final IExpertInviteAvoidRuleService expertInviteAvoidRuleService;

private final YxtCallOrSmsHelper yxtCallOrSmsHelper;

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

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

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

/**
* 项目重启之后重新初始化邀请任务
*/
private void initInviteTaskByStart() {
Set<Object> meetingIds = cachePlusOps.sMembers(CACHE_KEY);
if (meetingIds == null || meetingIds.isEmpty()) {
return;
}
for (Object meetingId : meetingIds) {
addInviteExpertTask(Convert.toLong(meetingId), true, properties.getInviteDelay());
}
}

/**
* 专家抽取校验
*
* @param meetingId 会议ID
* @return boolean
* @author WendyYang
**/
private boolean inviteCheck(Long meetingId) {
List<ExpertInviteRule> expertInviteRules = inviteRuleService.listByMeetingId(meetingId);
Function<ExpertInviteRule, ExpertInviteType> groupKey = w -> ExpertInviteType.getByCode(w.getInviteType());
Map<ExpertInviteType, List<ExpertInviteRule>> groupByType = CollUtils.group(expertInviteRules, groupKey);
List<ExpertInviteRule> randomRules = groupByType.get(ExpertInviteType.RANDOM);
if (CollUtil.isEmpty(randomRules)) {
return false;
}
Map<Long, ExpertInviteRule> ruleMap = CollUtils.listToMap(randomRules, ExpertInviteRule::getId);
LambdaQueryWrapper<MeetingExpert> query = Wrappers.lambdaQuery(MeetingExpert.class)
.in(MeetingExpert::getRuleId, ruleMap.keySet())
.in(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode());
List<MeetingExpert> meetingExperts = meetingExpertService.list(query);
int totalCount = CollUtils.sum(randomRules, ExpertInviteRule::getInviteCount);
boolean needed = totalCount > meetingExperts.size();
if (!needed) {
cancelByMeetingId(meetingId);
}
return needed;
}

/**
* 唤醒某个会议的抽取任务
*
* @param meetingId 会议ID
* @author WendyYang
**/
public void addInviteExpertTask(Long meetingId) {
boolean contains = INVITE_MAP.containsKey(meetingId);
if (!contains) {
addInviteExpertTask(meetingId, false, properties.getInviteDelay());
log.info("重置会议的随机抽取状态:{}", meetingId);
LambdaUpdateWrapper<Meeting> update = Wrappers.lambdaUpdate(Meeting.class);
update.set(Meeting::getStopRandomInvite, false);
update.set(Meeting::getUpdateBy, LoginUserUtil.getUserId());
update.set(Meeting::getUpdateOn, LocalDateTime.now());
update.eq(Meeting::getId, meetingId);
meetingService.update(update);
}
}

/**
* 添加专家抽取校验任务
*
* @param meetingId 会议ID
* @param checked 是否前置校验
* @param delayedMinutes 延迟执行时间
* @author WendyYang
**/
public void addInviteExpertTask(Long meetingId, boolean checked, int delayedMinutes) {
if (checked && !inviteCheck(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);
} catch (Exception e) {
log.error("执行专家邀请任务异常:{}", meetingId, e);
}
}, startTime, Duration.ofMinutes(properties.getInviteFixedRate()));
INVITE_MAP.putIfAbsent(meetingId, future);
log.info("添加专家抽取后台任务:{}", meetingId);
}

public void addInviteExpertTaskByMeetingCreate(Long meetingId, int delayedMinutes) {
Assert.isTrue(properties.getEnable(), "随机邀请已关闭");
addInviteExpertTask(meetingId, false, delayedMinutes);
cachePlusOps.sAdd(CACHE_KEY, meetingId);
}

@Transactional(rollbackFor = Exception.class)
public void invite(Long meetingId) {
log.info("开始进行专家后台抽取:{}", meetingId);
Meeting meeting = meetingService.getById(meetingId);
if (meeting.getStartTime().isBefore(LocalDateTime.now())) {
// 会议开始结束随机抽取
cancelByMeetingId(meetingId);
log.info("会议已开始停止抽取:{}", meeting);
return;
}
// 随机邀请规则
Map<Long, RandomInviteRuleDTO> ruleMap = inviteRuleService.randomRuleByMeetingId(meetingId);
// 回避规则
AvoidInfoDTO avoidInfoDto = expertInviteAvoidRuleService.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);
// 已确认参加、通话中数量
ExpertCntBO cnt = countMap.getOrDefault(ruleId, ExpertCntBO.zeroInit());
int tempCurrent = cnt.getAgreeCnt() + cnt.getNoticeCnt();
if (tempCurrent == 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);

if (expertChoose.getTotal() > 0) {
List<MeetingExpert> expertMeetings = CollUtils.convert(expertChoose.getExperts(), w -> {
MeetingExpert expert = ExpertInviteBuilder.getExpertByRandom(meetingId, w, ruleId);
expert.setPreStatus(ExpertAttendStatus.NOTICING.getCode());
expert.setStatus(ExpertAttendStatus.NOTICING.getCode());
return expert;
});
yxtCallOrSmsHelper.callByMeetingExperts(meeting, expertMeetings);
log.info("会议:{} 后台抽取专家:{}名", meetingId, expertMeetings.size());
meetingExpertService.saveBatch(expertMeetings);
} else {
// 抽取人数不够
notSupportCnt.incrementAndGet();
}
});
if (notIgnoreCnt.get() == 0 || notIgnoreCnt.get() == notSupportCnt.get()) {
if (notSupportCnt.get() > 0) {
// TODO
/*UserInfo inviterBasic = userInfoService.getById(meeting.getCreateBy());
UserInfo inviter = userInfoService.getById(inviterBasic.getId());
SendSmsContext context = YxtSmsContextBuilder.smsByRandomInviteStop(inviter.getNickname(), meeting.getName(), inviterBasic.getPhoneNo());
yxtCallOrSmsHelper.sendSms(context);*/
}
log.info("停止会议随机邀请:{} 未完成抽取规则数量 {} 无可抽取专家规则数量 {}", meetingId, notIgnoreCnt, notSupportCnt);
currProxy().cancelByMeetingId(meetingId);
}
}

@Transactional(rollbackFor = Exception.class)
public void cancelByMeetingId(Long meetingId) {
log.info("终止专家抽取:{}", meetingId);
meetingService.stopRandomInvite(meetingId);
cachePlusOps.sRem(CACHE_KEY, meetingId);
ScheduledFuture<?> future = INVITE_MAP.get(meetingId);
if (future != null) {
INVITE_MAP.remove(meetingId);
if (!future.isCancelled()) {
future.cancel(true);
}
}
}

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

private Map<Long, ExpertCntBO> countByAgree(Map<Long, MeetingExpert> expertMap, Map<Long, MeetingExpert> replacedMap) {
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 (ExpertAttendStatus.AGREED.eq(expert.getStatus())) {
cnt.incrAgreeCnt();
} else if (ExpertAttendStatus.NOTICING.eq(expert.getStatus())) {
cnt.incrNoticeCnt();
} else if (ExpertAttendStatus.REPLACED.eq(expert.getStatus())) {
MeetingExpert replacedExpert = replacedMap.get(expert.getId());
if (replacedExpert != null && ExpertAttendStatus.AGREED.eq(replacedExpert.getStatus())) {
cnt.incrAgreeCnt();
}
}
}
return cnt;
})));
}

@Data
public static class ExpertCntBO {

private Integer agreeCnt;

private Integer noticeCnt;

public ExpertCntBO(Integer agreeCnt, Integer noticeCnt) {
this.agreeCnt = agreeCnt;
this.noticeCnt = noticeCnt;
}

public void incrAgreeCnt() {
agreeCnt++;
}

public void incrNoticeCnt() {
noticeCnt++;
}

public static ExpertCntBO zeroInit() {
return new ExpertCntBO(0, 0);
}

}

}

+ 93
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/MeetingInviteCompleteNoticeTask.java 파일 보기

@@ -0,0 +1,93 @@
//package com.ningdatech.pmapi.meeting.task;
//
//import com.alibaba.fastjson.JSONObject;
//import com.baomidou.mybatisplus.core.toolkit.StringUtils;
//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
//import com.ningdatech.pmapi.meeting.builder.YxtSmsContextBuilder;
//import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
//import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
//import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus;
//import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus;
//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
//import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
//import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
//import com.ningdatech.pmapi.meeting.service.IMeetingService;
//import lombok.AllArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.PostConstruct;
//import java.time.Duration;
//import java.time.Instant;
//import java.time.temporal.ChronoUnit;
//import java.util.HashSet;
//import java.util.List;
//import java.util.Objects;
//import java.util.Set;
//
///**
// * @author liuxinxin
// * @date 2023/2/2 上午9:57
// */
//
//@Slf4j
//@Component
//@AllArgsConstructor
//public class MeetingInviteCompleteNoticeTask {
//
// private final RandomInviteProperties randomInviteProperties;
// private final IMeetingService meetingService;
// private final IMeetingExpertService meetingExpertService;
// private final ThreadPoolTaskScheduler scheduler;
// private final RedisUtils redisUtils;
// private final static String MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY = "MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY";
// private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
// private final IExpertInviteRuleService iExpertInviteRuleService;
//
// @PostConstruct
// public void initTask() {
// Instant startTime = Instant.now().plus(randomInviteProperties.getMeetingInviteCompleteNoticeRate(), ChronoUnit.MINUTES);
// // 处理电话结果回填
// scheduler.scheduleAtFixedRate(this::meetingInviteCompleteNotice, startTime, Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()));
// }
//
// public void meetingInviteCompleteNotice() {
// log.info("开始执行抽取完成通知:{}", Thread.currentThread().getName());
// List<Meeting> meetingIdList = meetingService.list(Wrappers.lambdaQuery(Meeting.class)
// .eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode())
// .eq(Meeting::getSendMeetingNotice, false)
// .eq(Meeting::getStopRandomInvite, true));
//
// Set<Long> completeNoticedMeetingIdList = getCompleteNoticedMeetingIdList();
// for (Meeting meeting : meetingIdList) {
// Long meetingId = meeting.getId();
// if (completeNoticedMeetingIdList.add(meetingId)) {
// List<ExpertInviteRule> expertInviteRules = iExpertInviteRuleService.listByMeetingId(meetingId);
// if (expertInviteRules.size() == 0) {
// return;
// }
// int noticeCount = meetingExpertService.countExpertByStatusAndMeetingId(ExpertAttendStatus.NOTICING, meetingId, null);
// if (noticeCount == 0) {
// redisUtils.set(MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY, JSONObject.toJSONString(completeNoticedMeetingIdList), 24 * 60 * 60);
// if (StringUtils.isNotBlank(meeting.getContact()) && StringUtils.isNotBlank(meeting.getConnecter())) {
// SendSmsCmd.SendSmsContext context = YxtSmsContextBuilder.meetingInviteCompleteNotice(meeting);
// yxtCallOrSmsHelper.sendSms(context);
// }
// }
// }
// }
// }
//
// private Set<Long> getCompleteNoticedMeetingIdList() {
// Object result = redisUtils.get(MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY);
// HashSet<Long> completeNoticedMeetingIdSet = new HashSet<>();
// if (Objects.nonNull(result)) {
// String resultStr = result.toString();
// List<Long> completeNoticedMeetingIdList = JSONObject.parseArray(resultStr, Long.class);
// completeNoticedMeetingIdSet = new HashSet<>(completeNoticedMeetingIdList);
// }
// return completeNoticedMeetingIdSet;
// }
//
//}

+ 160
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/NoticeExpertAt24HoursBeforeMeetingTask.java 파일 보기

@@ -0,0 +1,160 @@
//package com.ningdatech.pmapi.meeting.task;
//
//import cn.hutool.crypto.SecureUtil;
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
//import com.ningdatech.basic.util.CollUtils;
//import com.ningdatech.basic.util.StrPool;
//import com.ningdatech.cache.model.cache.CacheHashKey;
//import com.ningdatech.cache.repository.CachePlusOps;
//import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
//import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
//import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
//import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus;
//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
//import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
//import com.ningdatech.pmapi.meeting.service.IMeetingService;
//import com.ningdatech.pmapi.meta.helper.DictionaryCache;
//import lombok.AllArgsConstructor;
//import lombok.Data;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.scheduling.annotation.Scheduled;
//import org.springframework.stereotype.Component;
//
//import java.time.LocalDateTime;
//import java.time.temporal.ChronoUnit;
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//import java.util.stream.Collectors;
//
///**
// * <p>
// * NoticeExpertAt24HoursBeforeMeetingTask
// * </p>
// *
// * @author WendyYang
// * @since 09:16 2022/11/16
// */
//@Slf4j
//@Component
//@AllArgsConstructor
//public class NoticeExpertAt24HoursBeforeMeetingTask {
//
// @Data
// private static class NoticeValue {
//
// private LocalDateTime startTime;
//
// private String addressMd5;
//
// }
//
// private final IMeetingService meetingService;
// private final IMeetingExpertService meetingExpertService;
// private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
// private final DictionaryCache dictionaryCache;
// private final CachePlusOps cachePlusOps;
// private final IExpertUserFullInfoService expertUserFullInfoService;
//
// private static final String EXPERT_NOTICE_AT_24H_BEFORE_MEETING = "EXPERT_NOTICE_AT_24H_BEFORE_MEETING";
//
// private void addNoticeKey(Long meetingId, Map<String, Object> noticeValue, LocalDateTime startTime) {
// long mills = ChronoUnit.SECONDS.between(LocalDateTime.now(), startTime);
// CacheHashKey key = new CacheHashKey();
// key.setKey(EXPERT_NOTICE_AT_24H_BEFORE_MEETING + StrPool.COLON + meetingId);
// key.setField(mills);
// cachePlusOps.hSet(key, noticeValue);
// }
//
// private Map<Long, NoticeValue> getNoticeKey(Long meetingId) {
// Map<Object, Object> cache = cachePlusOps.hGet(EXPERT_NOTICE_AT_24H_BEFORE_MEETING + StrPool.COLON + meetingId);
// return cache.entrySet().stream().collect(Collectors.toMap(w -> Long.valueOf(w.getKey().toString()), w -> (NoticeValue) w.getValue()));
// }
//
// /**
// * 查询需要发送通知的会议:
// * <ul>1. 未完成</ul>
// * <ul>2. 创建邀请</ul>
// * <ul>3. 当前时间距离会议开始时间不足24小时</ul>
// *
// * @return 会议信息
// * @author WendyYang
// **/
// private Map<Long, Meeting> loadMeetingAt24HoursBeforeMeeting() {
// LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class);
// LocalDateTime now = LocalDateTime.now(), hoursAgoBy24 = now.plusHours(24);
// // 10分钟之内不再发送通知
// query.between(Meeting::getStartTime, now.plusMinutes(10), hoursAgoBy24);
// query.eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode());
// query.eq(Meeting::getInvited, Boolean.TRUE);
// List<Meeting> meetings = meetingService.list(query);
// return CollUtils.listToMap(meetings, Meeting::getId);
// }
//
// private String addressDetailMd5(Meeting meeting) {
// String content = meeting.getRegionCode() + StrPool.HASH + meeting.getRegionLevel() + StrPool.HASH + meeting.getRegionDetail();
// return SecureUtil.md5(content);
// }
//
// private boolean skipExpert(NoticeValue noticeValue, String addressMd5, LocalDateTime startTime) {
// boolean skip = noticeValue.getAddressMd5().equals(addressMd5);
// if (skip) {
// skip = noticeValue.getStartTime().equals(startTime);
// }
// return skip;
// }
//
// @Scheduled(cron = "${expert-attend-notice.cron:0 0/5 7-21 * * ?}")
// public void execute() {
// log.info("开始执行会议开始前24小时再次短信提醒任务");
// Map<Long, Meeting> meetingsMap = loadMeetingAt24HoursBeforeMeeting();
// if (meetingsMap.isEmpty()) {
// return;
// }
// List<MeetingExpert> expertList = meetingExpertService.listExpertByAgreeAttend(meetingsMap.keySet());
// if (expertList.isEmpty()) {
// return;
// }
// List<SendSmsCmd.SendSmsContext> contexts = new ArrayList<>();
// Map<Long, List<MeetingExpert>> expertGroup = CollUtils.group(expertList, MeetingExpert::getMeetingId);
// expertGroup.forEach((meetingId, experts) -> {
// Map<String, Object> noticeCache = new HashMap<>(16);
// Meeting meeting = meetingsMap.get(meetingId);
// String md5 = addressDetailMd5(meeting);
// Map<Long, NoticeValue> noticeKey = getNoticeKey(meetingId);
// String holdCompany = meeting.getHoldCompanyBracket();
// String startTime = meeting.getStartTime().format(DateUtil.DTF_YMD_HM);
// String meetingType = dictionaryCache.getByCode(meeting.getType()).getName();
// for (MeetingExpert expert : experts) {
// NoticeValue noticeValue = noticeKey.get(expert.getId());
// if (noticeValue != null) {
// if (skipExpert(noticeValue, md5, meeting.getStartTime())) {
// continue;
// }
// }
// NoticeValue value = new NoticeValue();
// value.setAddressMd5(md5);
// value.setStartTime(meeting.getStartTime());
// noticeCache.put(expert.getId().toString(), value);
// SendSmsCmd.SendSmsContext context = new SendSmsCmd.SendSmsContext();
//
// ExpertUserFullInfo expertUserFullInfo = expertUserFullInfoService.getByUserId(expert.getExpertId());
// String content = String.format(YxtSmsTemplateConst.SEND_MEETING_NOTICE, expertUserFullInfo.getName(),
// expert.getUpdateOn().format(DateUtil.DTF_YMD_HM), meetingType, startTime,
// meeting.getRegionDetail(), meeting.getContact());
// context.setContent(holdCompany + content);
// context.setReceiveNumber(expert.getMobile());
// contexts.add(context);
// }
// if (!noticeCache.isEmpty()) {
// addNoticeKey(meetingId, noticeCache, meeting.getStartTime());
// }
// });
// if (!contexts.isEmpty()) {
// yxtCallOrSmsHelper.sendSms(contexts);
// }
// }
//
//}

+ 40
- 0
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/RandomInviteProperties.java 파일 보기

@@ -0,0 +1,40 @@
package com.ningdatech.pmapi.meeting.task;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
* <p>
* TelInviteTaskProperties
* </p>
*
* @author WendyYang
* @since 17:25 2022/9/9
*/
@Data
@Component
@ConfigurationProperties(prefix = "random-invite")
public class RandomInviteProperties {

/**
* 是否开启随机抽取
*/
private Boolean enable = true;
/**
* 电话结果回填执行频率(分钟)
*/
private Integer resultRewriteFixedRate = 2;
/**
* 随机邀请延迟执行(分钟)
*/
private Integer inviteDelay = 2;
/**
* 随机邀请执行频率(分钟)
*/
private Integer inviteFixedRate = 3;
/**
* 会议抽取完成通知 管理员下发会议通知
*/
private Integer meetingInviteCompleteNoticeRate = 1;
}

+ 6
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/util/LoginUserUtil.java 파일 보기

@@ -2,6 +2,7 @@ package com.ningdatech.pmapi.user.util;

import com.ningdatech.basic.auth.AbstractLoginUserUtil;
import com.ningdatech.pmapi.sys.model.entity.Role;
import com.ningdatech.pmapi.user.entity.enumeration.RoleEnum;
import com.ningdatech.pmapi.user.security.auth.model.UserInfoDetails;

import java.util.List;
@@ -25,4 +26,9 @@ public class LoginUserUtil extends AbstractLoginUserUtil {
return userRoleList.stream().map(Role::getId).collect(Collectors.toList());
}

public static boolean isExpert() {
UserInfoDetails detail = loginUserDetail();
return detail.getUserRoleList().stream().anyMatch(w -> RoleEnum.EXPERT.eq(w.getCode()));
}

}

불러오는 중...
취소
저장