@@ -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; | |||
} |
@@ -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); | |||
}))); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
// } | |||
//} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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() + ")"; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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 { | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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("无效的邀请状态")); | |||
} | |||
} |
@@ -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("无效的邀请类型")); | |||
} | |||
} |
@@ -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("无效的会议日期类型编码")); | |||
} | |||
} |
@@ -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("状态编码")); | |||
} | |||
} | |||
} |
@@ -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 | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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)); | |||
}*/ | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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> { | |||
} |
@@ -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> |
@@ -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> { | |||
} |
@@ -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> |
@@ -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); | |||
} |
@@ -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> |
@@ -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); | |||
} |
@@ -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> |
@@ -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); | |||
} |
@@ -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() < start_time) THEN 'TO_BE_HELD' | |||
WHEN (status = 1 AND now() >= 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> |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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))); | |||
} | |||
} |
@@ -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())); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
// }); | |||
// } | |||
// }*/ | |||
// } | |||
// | |||
//} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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; | |||
// } | |||
// | |||
//} |
@@ -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); | |||
// } | |||
// } | |||
// | |||
//} |
@@ -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; | |||
} |
@@ -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())); | |||
} | |||
} |