diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/CommonConstant.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/CommonConstant.java index becadbe..7898300 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/CommonConstant.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/constant/CommonConstant.java @@ -14,4 +14,8 @@ public class CommonConstant { public static final String CALL_STATUS = "status"; public static final String CALL_STATUS_OK_VALUE = "ok"; + public static final Integer COMMENT_MAX_SIZE = 163; + public static final Integer SUB_COMMENT_SIZE_MIN = 0; + public static final Integer SUB_COMMENT_SIZE_MAX = 160; + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/CountGroupByDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/CountGroupByDTO.java new file mode 100644 index 0000000..9c4de54 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/model/entity/CountGroupByDTO.java @@ -0,0 +1,20 @@ +package com.ningdatech.pmapi.common.model.entity; + +import lombok.Data; + +/** + *

+ * CountGroupByDto + *

+ * + * @author WendyYang + * @since 15:44 2022/8/28 + */ +@Data +public class CountGroupByDTO { + + private Integer total; + + private T groupKey; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/util/BizUtils.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/BizUtils.java index 66d8e18..15d5ec3 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/common/util/BizUtils.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/BizUtils.java @@ -5,13 +5,13 @@ import com.ningdatech.basic.util.StrPool; import org.apache.commons.lang3.StringUtils; import org.springframework.util.NumberUtils; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toList; + /** *

* BizUtils @@ -26,6 +26,12 @@ public class BizUtils { } + public static > void notEmpty(T objs, Consumer consumer) { + if (objs != null && !objs.isEmpty()) { + consumer.accept(objs); + } + } + public static List splitToNum(String str, Class aClass) { if (StrUtil.isEmpty(str)) { return Collections.emptyList(); @@ -46,7 +52,7 @@ public class BizUtils { } } - public static void notBlank(String str,Consumer consumer) { + public static void notBlank(String str, Consumer 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 + * @author WendyYang + **/ + public static Collection groupFirst(Collection objs, Function group, Comparator comparator) { + return groupFirstMap(objs, group, comparator).values(); + } + + /** + * 对象分组取第一条 + * + * @param objs 对象集合 + * @param group 分组函数 + * @param comparator 比较器 + * @return java.util.Collection + * @author WendyYang + **/ + public static Map groupFirstMap(Collection objs, Function group, Comparator comparator) { + return objs.stream().collect(Collectors.groupingBy(group, + Collectors.collectingAndThen(toList(), w -> { + w.sort(comparator); + return w.get(0); + }))); + } + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/util/StrUtils.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/StrUtils.java new file mode 100644 index 0000000..f034ed1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/util/StrUtils.java @@ -0,0 +1,29 @@ +package com.ningdatech.pmapi.common.util; + +import cn.hutool.core.text.StrPool; +import cn.hutool.core.util.StrUtil; + +import java.util.Collections; +import java.util.List; + +/** + *

+ * StrUtils + *

+ * + * @author WendyYang + * @since 21:23 2023/2/23 + */ +public class StrUtils extends StrUtil { + + private StrUtils() { + } + + public static List split(String str) { + if (isBlank(str)) { + return Collections.emptyList(); + } + return split(str, StrPool.COMMA); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/expert/helper/PermissionCheckHelper.java b/pmapi/src/main/java/com/ningdatech/pmapi/expert/helper/PermissionCheckHelper.java new file mode 100644 index 0000000..bb6a413 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/expert/helper/PermissionCheckHelper.java @@ -0,0 +1,20 @@ +package com.ningdatech.pmapi.expert.helper; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * @author liuxinxin + * @date 2022/8/18 上午10:02 + * 校验是否一个账号对一个专家有操作的权限 + */ +@Component +@RequiredArgsConstructor +public class PermissionCheckHelper { + + public boolean isSuperAdmin() { + // TODO + return false; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertIntentionWorkRegionService.java b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertIntentionWorkRegionService.java index 6c1a9b5..b597d54 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertIntentionWorkRegionService.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertIntentionWorkRegionService.java @@ -3,6 +3,8 @@ package com.ningdatech.pmapi.expert.service; import com.ningdatech.pmapi.expert.entity.ExpertIntentionWorkRegion; import com.baomidou.mybatisplus.extension.service.IService; +import java.util.List; + /** *

* 服务类 @@ -13,4 +15,14 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface IExpertIntentionWorkRegionService extends IService { + /** + * 根据履职意向地或许满足履职意向地的用户ID集合 + * + * @param regionCode regionCode + * @param regionLevel regionLevel + * @return java.util.List + * @author WendyYang + **/ + List userIdsMatchIntentionRegion(String regionCode, Integer regionLevel); + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertUserFullInfoService.java b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertUserFullInfoService.java index 7b5960a..af68be9 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertUserFullInfoService.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/IExpertUserFullInfoService.java @@ -1,11 +1,13 @@ package com.ningdatech.pmapi.expert.service; -import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; import com.baomidou.mybatisplus.extension.service.IService; +import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; + +import java.util.List; /** *

- * 服务类 + * 服务类 *

* * @author Liuxinxin @@ -21,4 +23,13 @@ public interface IExpertUserFullInfoService extends IService * @return / **/ ExpertUserFullInfo getByUserId(Long userId); + + /** + * 查询用户信息 + * + * @param userId 用户ID + * @return / + **/ + List listByUserId(List userId); + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertIntentionWorkRegionServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertIntentionWorkRegionServiceImpl.java index 74403d9..244f97d 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertIntentionWorkRegionServiceImpl.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertIntentionWorkRegionServiceImpl.java @@ -1,14 +1,19 @@ package com.ningdatech.pmapi.expert.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ningdatech.basic.util.CollUtils; import com.ningdatech.pmapi.expert.entity.ExpertIntentionWorkRegion; import com.ningdatech.pmapi.expert.mapper.ExpertIntentionWorkRegionMapper; import com.ningdatech.pmapi.expert.service.IExpertIntentionWorkRegionService; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; +import java.util.List; + /** *

- * 服务实现类 + * 服务实现类 *

* * @author Liuxinxin @@ -17,4 +22,14 @@ import org.springframework.stereotype.Service; @Service public class ExpertIntentionWorkRegionServiceImpl extends ServiceImpl implements IExpertIntentionWorkRegionService { + @Override + public List userIdsMatchIntentionRegion(String regionCode, Integer regionLevel) { + LambdaQueryWrapper intentionQuery = Wrappers.lambdaQuery(ExpertIntentionWorkRegion.class) + .eq(ExpertIntentionWorkRegion::getRegionCode, regionCode) + .eq(ExpertIntentionWorkRegion::getRegionLevel, regionLevel) + .select(ExpertIntentionWorkRegion::getUserId); + List userIdMatchIntentionRegion = list(intentionQuery); + return CollUtils.fieldList(userIdMatchIntentionRegion, ExpertIntentionWorkRegion::getUserId); + } + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertUserFullInfoServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertUserFullInfoServiceImpl.java index 4b22e1f..69544bc 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertUserFullInfoServiceImpl.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/expert/service/impl/ExpertUserFullInfoServiceImpl.java @@ -28,4 +28,9 @@ public class ExpertUserFullInfoServiceImpl extends ServiceImpllambdaQuery().eq(ExpertUserFullInfo::getUserId, userId)); } + @Override + public List listByUserId(List userIds) { + return list(Wrappers.lambdaQuery().in(ExpertUserFullInfo::getUserId, userIds)); + } + } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/ExpertInviteBuilder.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/ExpertInviteBuilder.java new file mode 100644 index 0000000..1191f09 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/ExpertInviteBuilder.java @@ -0,0 +1,40 @@ +package com.ningdatech.pmapi.meeting.builder; + +import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +import com.ningdatech.pmapi.user.entity.UserInfo; + +/** + *

+ * ExpertInviteBuilder + *

+ * + * @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); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/YxtSmsContextBuilder.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/YxtSmsContextBuilder.java new file mode 100644 index 0000000..4d150cd --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/builder/YxtSmsContextBuilder.java @@ -0,0 +1,129 @@ +//package com.ningdatech.pmapi.meeting.builder; +// +// +//import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +//import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +//import com.ningdatech.pmapi.sms.utils.DateUtil; +// +//import java.time.format.DateTimeFormatter; +//import java.util.ArrayList; +//import java.util.List; +// +///** +// *

+// * YxtSmsContextBuilder +// *

+// * +// * @author WendyYang +// * @since 14:19 2022/11/17 +// */ +//public class YxtSmsContextBuilder { +// +// private YxtSmsContextBuilder() { +// } +// +// public static List smsToExpertByCancelMeeting(Meeting meeting, List experts, String meetingType) { +// String holdCompany = meeting.getHoldCompanyBracket(); +// String meetingTime = meeting.getStartTime().format(DateUtil.DTF_YMD_HM); +// List 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 smsToExpertBySendNotice(Meeting meeting, List experts, String meetingType) { +// String holdCompany = meeting.getHoldCompanyBracket(); +// String meetingTime = meeting.getStartTime().format(DateUtil.DTF_YMD_HM); +// List 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 smsToExpertByMeetingChange(Meeting old, Meeting curr, List experts, String meetingType) { +// List 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; +// } +//} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/DashboardController.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/DashboardController.java new file mode 100644 index 0000000..3841f7c --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/DashboardController.java @@ -0,0 +1,77 @@ +package com.ningdatech.pmapi.meeting.controller; + +import com.ningdatech.basic.model.PagePo; +import com.ningdatech.basic.model.PageVo; +import com.ningdatech.log.annotation.WebLog; +import com.ningdatech.pmapi.meeting.entity.req.MeetingCalenderReq; +import com.ningdatech.pmapi.meeting.entity.vo.*; +import com.ningdatech.pmapi.meeting.manage.DashboardManage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; + +/** + *

+ * DashboardController + *

+ * + * @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 meetingCalender(@Valid MeetingCalenderReq po) { + return dashboardManage.meetingCalender(po); + } + + @ApiOperation("待办:专家评价列表") + @GetMapping("/todo/expertEvaluation") + @WebLog(value = "待办:专家评价列表") + public PageVo expertEvaluationToDo(PagePo po) { + return dashboardManage.expertEvaluationToDo(po); + } + + @ApiOperation("待办:专家待替换列表") + @GetMapping("/todo/expertReplace") + @WebLog(value = "待办:专家待替换列表") + public PageVo expertReplaceToDo(PagePo po) { + return dashboardManage.expertReplaceTodoList(po); + } + + @ApiOperation("待办:专家待确认列表") + @GetMapping("/todo/expertConfirm") + @WebLog(value = "待办:专家待确认列表") + public PageVo 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(); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java new file mode 100644 index 0000000..eb2e25e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java @@ -0,0 +1,173 @@ +package com.ningdatech.pmapi.meeting.controller; + + +import com.ningdatech.basic.model.IdVo; +import com.ningdatech.basic.model.PageVo; +import com.ningdatech.log.annotation.WebLog; +import com.ningdatech.pmapi.meeting.entity.dto.MeetingBasicDTO; +import com.ningdatech.pmapi.meeting.entity.req.*; +import com.ningdatech.pmapi.meeting.entity.vo.*; +import com.ningdatech.pmapi.meeting.manage.MeetingManage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.AllArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + *

+ * 会议 前端控制器 + *

+ * + * @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 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 meetingListByManager(MeetingListReq po) { + return meetingManage.meetingListByManager(po); + } + + @ApiOperation("履职记录 | 专家会议列表") + @GetMapping("/meetingListByExpert") + @WebLog(value = "履职记录 | 专家会议列表") + public PageVo 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); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/config/WebProperties.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/config/WebProperties.java new file mode 100644 index 0000000..8729d6e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/config/WebProperties.java @@ -0,0 +1,36 @@ +package com.ningdatech.pmapi.meeting.entity.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + *

+ * SystemProperties + *

+ * + * @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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteAvoidRule.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteAvoidRule.java new file mode 100644 index 0000000..dd99ef2 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteAvoidRule.java @@ -0,0 +1,56 @@ +package com.ningdatech.pmapi.meeting.entity.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * ExpertInviteAvoidRule + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteIgnoreTime.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteIgnoreTime.java new file mode 100644 index 0000000..a70a9f2 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteIgnoreTime.java @@ -0,0 +1,54 @@ +package com.ningdatech.pmapi.meeting.entity.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.time.LocalTime; + +/** + *

+ * 专家抽取免打扰时间设置 + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteRule.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteRule.java new file mode 100644 index 0000000..f317cc8 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/ExpertInviteRule.java @@ -0,0 +1,55 @@ +package com.ningdatech.pmapi.meeting.entity.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * ExpertInviteRule + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/Meeting.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/Meeting.java new file mode 100644 index 0000000..d09e155 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/Meeting.java @@ -0,0 +1,116 @@ +package com.ningdatech.pmapi.meeting.entity.domain; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 会议-实体 + *

+ * + * @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() + ")"; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java new file mode 100644 index 0000000..ce7f9a1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpert.java @@ -0,0 +1,73 @@ +package com.ningdatech.pmapi.meeting.entity.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 事务专家表 + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpertEvaluation.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpertEvaluation.java new file mode 100644 index 0000000..c8462aa --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/domain/MeetingExpertEvaluation.java @@ -0,0 +1,66 @@ +package com.ningdatech.pmapi.meeting.entity.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.ningdatech.pmapi.sys.model.entity.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 专家评价表 + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AbstractInviteRule.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AbstractInviteRule.java new file mode 100644 index 0000000..bd965b5 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AbstractInviteRule.java @@ -0,0 +1,39 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + *

+ * AbstractExpertExtractRule + *

+ * + * @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 { + } + + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AppointInviteRuleDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AppointInviteRuleDTO.java new file mode 100644 index 0000000..f322835 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AppointInviteRuleDTO.java @@ -0,0 +1,34 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + *

+ * AppointExtractRuleDto + *

+ * + * @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 expertIds; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidInfoDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidInfoDTO.java new file mode 100644 index 0000000..97c4d5e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/AvoidInfoDTO.java @@ -0,0 +1,60 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Collections; +import java.util.List; + +/** + *

+ * AvoidInfoDto + *

+ * + * @author WendyYang + * @since 10:22 2022/7/26 + */ +@Data +@ApiModel("回避信息") +public class AvoidInfoDTO { + + @ApiModelProperty("回避单位") + @NotEmpty(message = "回避单位不能为空", groups = {AbstractInviteRule.RuleSave.class}) + private List companyIds; + + @ApiModelProperty("是否回避同单位其他专家") + @NotNull(message = "是否回避同单位其他专家不能为空") + private Boolean avoidMates; + + @ApiModelProperty("回避专家ID") + private List 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()); + } + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/CountConfirmByMeetingIdDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/CountConfirmByMeetingIdDTO.java new file mode 100644 index 0000000..af9ab8a --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/CountConfirmByMeetingIdDTO.java @@ -0,0 +1,31 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +/** + *

+ * CountConfirmByMeetingIdDto + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertChooseDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertChooseDTO.java new file mode 100644 index 0000000..e76145e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertChooseDTO.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +/** + *

+ * ExpertChooseDto + *

+ * + * @author WendyYang + * @since 00:44 2022/7/27 + */ +@Data +@AllArgsConstructor +public class ExpertChooseDTO { + + private List experts; + + private Integer total; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertDictChooseDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertDictChooseDTO.java new file mode 100644 index 0000000..053c533 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertDictChooseDTO.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + *

+ * ExpertDictChooseDTO + *

+ * + * @author WendyYang + * @since 15:49 2022/8/9 + */ +@Data +public class ExpertDictChooseDTO { + + @ApiModelProperty("专家字典类型") + private String expertDict; + + @ApiModelProperty("字典编码") + private List dictCodes; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertTagChooseDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertTagChooseDTO.java new file mode 100644 index 0000000..6f10cf0 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/ExpertTagChooseDTO.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + *

+ * ExpertTagChooseDTO + *

+ * + * @author WendyYang + * @since 19:45 2022/7/26 + */ +@Data +public class ExpertTagChooseDTO { + + @ApiModelProperty("专家标签") + private String expertTag; + + @ApiModelProperty("标签编码") + private List tagCodes; + +} \ No newline at end of file diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingAndAttendStatusDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingAndAttendStatusDTO.java new file mode 100644 index 0000000..9991234 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingAndAttendStatusDTO.java @@ -0,0 +1,22 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import lombok.Data; + +/** + *

+ * MeetingAndAttendStatusDto + *

+ * + * @author WendyYang + * @since 22:48 2022/8/22 + */ +@Data +public class MeetingAndAttendStatusDTO { + + private Long meetingId; + + private Integer status; + + private Boolean attended; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java new file mode 100644 index 0000000..8107ea8 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java @@ -0,0 +1,80 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * MeetingBasicDto + *

+ * + * @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 attachmentIds; + + @ApiModelProperty("备注") + private String remark; + + @ApiModelProperty("创建人") + private String author; + + @ApiModelProperty("创建时间") + private LocalDateTime createOn; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/RandomInviteRuleDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/RandomInviteRuleDTO.java new file mode 100644 index 0000000..a7c1ee5 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/RandomInviteRuleDTO.java @@ -0,0 +1,46 @@ +package com.ningdatech.pmapi.meeting.entity.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + *

+ * RandomExtractRuleDto + *

+ * + * @author WendyYang + * @since 10:18 2022/7/26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel("随机抽取规则") +public class RandomInviteRuleDTO extends AbstractInviteRule { + + @ApiModelProperty("专家字典信息") + private List expertDicts; + + @ApiModelProperty("履职意向地编码") + private String intentionRegionCode; + + @ApiModelProperty("履职意向地层级") + private Integer intentionRegionLevel; + + @ApiModelProperty("专家层级编码") + private String expertRegionCode; + + @ApiModelProperty("专家层级级别") + private Integer expertRegionLevel; + + @ApiModelProperty("专家标签") + private List expertTags; + + @ApiModelProperty("专家最长响应时间") + @NotNull(message = "专家最长响应时间不能为空", groups = {RuleSave.class}) + private Integer waitForCallbackMinutes; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatus.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatus.java new file mode 100644 index 0000000..095af8c --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertAttendStatus.java @@ -0,0 +1,45 @@ +package com.ningdatech.pmapi.meeting.entity.enumeration; + +import lombok.Getter; + +import java.util.Arrays; + +/** + *

+ * ExpertAttendStatus + *

+ * + * @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("无效的邀请状态")); + } + +} \ No newline at end of file diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertInviteType.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertInviteType.java new file mode 100644 index 0000000..fde33e9 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/ExpertInviteType.java @@ -0,0 +1,39 @@ +package com.ningdatech.pmapi.meeting.entity.enumeration; + +import lombok.Getter; + +import java.util.Arrays; + +/** + *

+ * ExpertExtractRuleType + *

+ * + * @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("无效的邀请类型")); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingDateTermType.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingDateTermType.java new file mode 100644 index 0000000..f846be9 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingDateTermType.java @@ -0,0 +1,36 @@ +package com.ningdatech.pmapi.meeting.entity.enumeration; + +import lombok.Getter; + +import java.util.Arrays; + +/** + *

+ * MeetingDateTermType-会议日期类型 + *

+ * + * @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("无效的会议日期类型编码")); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatus.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatus.java new file mode 100644 index 0000000..e90255f --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatus.java @@ -0,0 +1,73 @@ +package com.ningdatech.pmapi.meeting.entity.enumeration; + +import lombok.Getter; + +import java.util.Arrays; + +/** + *

+ * MeetingStatus + *

+ * + * @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("状态编码")); + } + + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatusByDashboard.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatusByDashboard.java new file mode 100644 index 0000000..1aeac9e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/enumeration/MeetingStatusByDashboard.java @@ -0,0 +1,30 @@ +package com.ningdatech.pmapi.meeting.entity.enumeration; + +/** + *

+ * MeetingStatusByDashboard + *

+ * + * @author WendyYang + * @since 14:02 2022/8/30 + */ +public enum MeetingStatusByDashboard { + + /** + * 已完成 + */ + COMPLETED, + /** + * 已取消 + */ + CANCELED, + /** + * 待召开 + */ + TO_BE_HELD, + /** + * 已召开 + */ + HELD + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/BatchAppointExpertsReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/BatchAppointExpertsReq.java new file mode 100644 index 0000000..155c4cb --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/BatchAppointExpertsReq.java @@ -0,0 +1,31 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + *

+ * BatchAppointExpertsPo + *

+ * + * @author WendyYang + * @since 15:49 2022/8/26 + */ +@Data +@ApiModel("补充专家参数实体") +public class BatchAppointExpertsReq { + + @NotEmpty(message = "专家ID不能为空") + @ApiModelProperty("专家ID") + private List expertIds; + + @NotNull(message = "会议ID不能为空") + @ApiModelProperty("会议ID") + private Long meetingId; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertEvaluationReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertEvaluationReq.java new file mode 100644 index 0000000..c882d33 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertEvaluationReq.java @@ -0,0 +1,49 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + *

+ * ExpertEvaluationPo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteCreateReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteCreateReq.java new file mode 100644 index 0000000..220654e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteCreateReq.java @@ -0,0 +1,43 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import com.ningdatech.pmapi.meeting.entity.dto.AbstractInviteRule; +import com.ningdatech.pmapi.meeting.entity.dto.AppointInviteRuleDTO; +import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO; +import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + *

+ * MeetingCreatePo + *

+ * + * @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 randomRules; + + @Valid + @ApiModelProperty("指定抽取规则") + private AppointInviteRuleDTO appointRule; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteIgnoreTimeModifyReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteIgnoreTimeModifyReq.java new file mode 100644 index 0000000..8fa5833 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertInviteIgnoreTimeModifyReq.java @@ -0,0 +1,36 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalTime; + +/** + *

+ * 专家抽取免打扰时间设置 + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertRemoveReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertRemoveReq.java new file mode 100644 index 0000000..ad08f87 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/ExpertRemoveReq.java @@ -0,0 +1,28 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * ExpertRemovePo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingBasicInfoModifyReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingBasicInfoModifyReq.java new file mode 100644 index 0000000..274d3ea --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingBasicInfoModifyReq.java @@ -0,0 +1,82 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * MeetingBasicInfoModifyPo + *

+ * + * @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 attachmentIds; + + @ApiModelProperty("备注") + private String remark; + + @ApiModelProperty("创建人") + private String author; + + @ApiModelProperty("创建时间") + private LocalDateTime createOn; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCalenderReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCalenderReq.java new file mode 100644 index 0000000..03e1f68 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCalenderReq.java @@ -0,0 +1,28 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.time.LocalDate; + +/** + *

+ * MeetingCalenderPo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCancelReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCancelReq.java new file mode 100644 index 0000000..47ebb97 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingCancelReq.java @@ -0,0 +1,30 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + *

+ * MeetingCancelPo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListByExpertReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListByExpertReq.java new file mode 100644 index 0000000..ce2003a --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListByExpertReq.java @@ -0,0 +1,21 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import com.ningdatech.basic.model.PagePo; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * MeetingListByExpertPo + *

+ * + * @author WendyYang + * @since 17:02 2022/8/23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class MeetingListByExpertReq extends PagePo { + + private Long expertId; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java new file mode 100644 index 0000000..b472592 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java @@ -0,0 +1,49 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import com.ningdatech.basic.model.PagePo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +/** + *

+ * MeetingListByManagerPo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingResultReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingResultReq.java new file mode 100644 index 0000000..ee600c7 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingResultReq.java @@ -0,0 +1,34 @@ +package com.ningdatech.pmapi.meeting.entity.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + *

+ * MeetingResultPo + *

+ * + * @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 attachments; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertAttendSummaryVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertAttendSummaryVO.java new file mode 100644 index 0000000..d3323df --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertAttendSummaryVO.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * ExpertAttendSummaryVo + *

+ * + * @author WendyYang + * @since 11:18 2022/8/28 + */ +@Data +public class ExpertAttendSummaryVO { + + private transient Long expertId; + + @ApiModelProperty("专家姓名") + private String expertName; + + @ApiModelProperty("专家出席次数") + private Integer attendCount; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertBasicInfoVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertBasicInfoVO.java new file mode 100644 index 0000000..aecb8e9 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertBasicInfoVO.java @@ -0,0 +1,48 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.ningdatech.pmapi.sys.model.dto.RegionDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +import java.util.List; + +/** + *

+ * ExpertBasicInfoVo + *

+ * + * @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 regions; + + @ApiModelProperty("职称级别") + private String jobLevel; + + @ApiModelProperty("单位属性") + private String companyType; + + @ApiModelProperty("工作单位") + private String company; + + @ApiModelProperty("联系方式") + private String contact; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertCountOnChangeVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertCountOnChangeVO.java new file mode 100644 index 0000000..6646d75 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertCountOnChangeVO.java @@ -0,0 +1,38 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + *

+ * ExpertCountOnChangeVo + *

+ * + * @author WendyYang + * @since 10:02 2022/8/8 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@ApiModel("专家抽取数量校验实体") +public class ExpertCountOnChangeVO { + + @ApiModelProperty("每个随机规则对应的可抽取数量") + private List countList; + + @ApiModelProperty("参数是否通过校验:true 是、false 否") + private Boolean status; + + @ApiModelProperty("错误提醒") + private String message; + + public void addCountList(Integer count) { + this.countList.add(count); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationListVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationListVO.java new file mode 100644 index 0000000..cf0113c --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationListVO.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * ExpertEvaluationListVo + *

+ * + * @author WendyYang + * @since 16:03 2022/8/8 + */ +@Data +@ApiModel("专家评价列表") +public class ExpertEvaluationListVO { + + @ApiModelProperty("专家评价ID") + private Long evaluationId; + + @ApiModelProperty("专家信息") + private ExpertBasicInfoVO expert; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationToDoListItemVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationToDoListItemVO.java new file mode 100644 index 0000000..f7e1b48 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertEvaluationToDoListItemVO.java @@ -0,0 +1,38 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +/** + *

+ * ExpertEvaluationToDoListItemVo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInvitationDetailVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInvitationDetailVO.java new file mode 100644 index 0000000..35c6b62 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInvitationDetailVO.java @@ -0,0 +1,60 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.alibaba.fastjson.annotation.JSONField; +import com.ningdatech.pmapi.sys.model.dto.RegionDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * ExpertInvitationDetailVo + *

+ * + * @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 regions; + + @ApiModelProperty("联系方式") + private String contact; + + @ApiModelProperty("联系人") + private String connecter; + + @ApiModelProperty("专家名称") + private String expertName; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteDetailVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteDetailVO.java new file mode 100644 index 0000000..672dfcc --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteDetailVO.java @@ -0,0 +1,95 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * ExpertInviteDetailVo + *

+ * + * @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 attendList = new ArrayList<>(); + + @ApiModelProperty("随机邀请名单") + private List randomInviteList = new ArrayList<>(); + + @ApiModelProperty("指定邀请名单") + private List 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); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteIgnoreTimeListVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteIgnoreTimeListVO.java new file mode 100644 index 0000000..606f133 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertInviteIgnoreTimeListVO.java @@ -0,0 +1,43 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.time.LocalTime; + +/** + *

+ * 专家抽取免打扰时间设置 + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertListItemFoEvaluationVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertListItemFoEvaluationVO.java new file mode 100644 index 0000000..f2bcfed --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertListItemFoEvaluationVO.java @@ -0,0 +1,30 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * MeetingExpertListItemFoEvaluationVo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertReplaceTodoListItemVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertReplaceTodoListItemVO.java new file mode 100644 index 0000000..9325bab --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/ExpertReplaceTodoListItemVO.java @@ -0,0 +1,41 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +/** + *

+ * ExpertReplaceTodoListItemVo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/InviteRuleDetailVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/InviteRuleDetailVO.java new file mode 100644 index 0000000..267127d --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/InviteRuleDetailVO.java @@ -0,0 +1,72 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + *

+ * InviteRuleDetailVo + *

+ * + * @author WendyYang + * @since 14:58 2022/8/17 + */ +@Data +public class InviteRuleDetailVO { + + @ApiModelProperty("是否创建邀请规则") + private Boolean invited; + + @ApiModelProperty("随机邀请规则") + private List randomRules; + + @ApiModelProperty("是否有指定抽取规则") + private Boolean hasAppointRule; + + @ApiModelProperty("指定抽取规则") + private AppointRuleVo appointRule; + + @ApiModelProperty("是否有回避规则") + private Boolean hasAvoidInfo; + + @ApiModelProperty("回避信息") + private AvoidInfoVo avoidInfo; + + @Data + public static class AvoidInfoVo { + + private List companyIds; + + private Boolean avoidMates; + + private List experts; + + } + + @Data + public static class AppointRuleVo { + + private String inviteDesc; + + private List 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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java new file mode 100644 index 0000000..11a2f52 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingByManagerVO.java @@ -0,0 +1,76 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +import java.time.LocalDateTime; + +/** + *

+ * MeetingByManagerVo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCalenderItemVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCalenderItemVO.java new file mode 100644 index 0000000..76607ea --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCalenderItemVO.java @@ -0,0 +1,38 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.List; + +/** + *

+ * MeetingCalenderItemVo + *

+ * + * @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 leaveIds; + + @ApiModelProperty("是否有会议") + private Boolean hasMeeting; + + @ApiModelProperty("会议信息") + private List meetings; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingConfirmToDoListItemVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingConfirmToDoListItemVO.java new file mode 100644 index 0000000..499d7e1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingConfirmToDoListItemVO.java @@ -0,0 +1,51 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.ningdatech.pmapi.sys.model.dto.RegionDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * MeetingConfirmToDoListItem + *

+ * + * @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 regions; + + @ApiModelProperty("状态") + private Integer status; + + @ApiModelProperty("邀请总人数") + private Integer totalExpert; + + @ApiModelProperty("确认总人数") + private Integer confirmedExpert; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountByExpertVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountByExpertVO.java new file mode 100644 index 0000000..c36eef6 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountByExpertVO.java @@ -0,0 +1,46 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + *

+ * MeetingCountByExpertVo + *

+ * + * @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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountSummaryVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountSummaryVO.java new file mode 100644 index 0000000..5cc643e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingCountSummaryVO.java @@ -0,0 +1,33 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; + +/** + *

+ * MeetingCountSummaryVo + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingDetailBasicVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingDetailBasicVO.java new file mode 100644 index 0000000..1fe9e37 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingDetailBasicVO.java @@ -0,0 +1,97 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.alibaba.fastjson.annotation.JSONField; +import com.ningdatech.file.entity.vo.result.AttachFileVo; +import com.ningdatech.pmapi.sys.model.dto.RegionDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.Tolerate; + +import java.time.LocalDateTime; +import java.util.List; + +/** + *

+ * MeetingDetailBasicVo + *

+ * + * @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 regions; + + @ApiModelProperty("地点") + private String address; + + @ApiModelProperty("联系方式") + private String contact; + + @ApiModelProperty("创建时间") + private LocalDateTime createOn; + + @ApiModelProperty("会议说明") + private String description; + + @ApiModelProperty("相关材料") + private List 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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingResultVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingResultVO.java new file mode 100644 index 0000000..66be470 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/MeetingResultVO.java @@ -0,0 +1,29 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.ningdatech.file.entity.vo.result.AttachFileVo; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + *

+ * MeetingResultVo + *

+ * + * @author WendyYang + * @since 21:28 2022/8/8 + */ +@Data +public class MeetingResultVO { + + @ApiModelProperty("事务ID") + private Long meetingId; + + @ApiModelProperty("会议结果说明") + private String resultDescription; + + @ApiModelProperty("附件信息") + private List attachments; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/RandomInviteRuleVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/RandomInviteRuleVO.java new file mode 100644 index 0000000..7d1f745 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/vo/RandomInviteRuleVO.java @@ -0,0 +1,31 @@ +package com.ningdatech.pmapi.meeting.entity.vo; + +import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO; +import com.ningdatech.pmapi.sys.model.dto.RegionDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + *

+ * RandomInviteRuleVo + *

+ * + * @author WendyYang + * @since 09:55 2022/8/26 + */ +@Data +@ApiModel("随机抽取规则视图实体") +@EqualsAndHashCode(callSuper = true) +public class RandomInviteRuleVO extends RandomInviteRuleDTO { + + @ApiModelProperty("履职意向地(回显使用)") + private List intentionRegions; + + @ApiModelProperty("专家层级意向地(回显使用)") + private List expertRegions; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java new file mode 100644 index 0000000..997f565 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/ExpertInviteHelper.java @@ -0,0 +1,80 @@ +package com.ningdatech.pmapi.meeting.helper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import com.ningdatech.pmapi.meeting.service.IMeetingService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.*; + +/** + *

+ * ExpertInviteHelper + *

+ * + * @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 + * @author WendyYang + **/ + public List listInvitedExpertByTime(LocalDateTime start, LocalDateTime end) { + LambdaQueryWrapper 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 meetings = meetingService.list(meetingQuery); + if (meetings.isEmpty()) { + return Collections.emptyList(); + } + List meetingIds = CollUtils.fieldList(meetings, Meeting::getId); + LambdaQueryWrapper meetingExpertQuery = Wrappers.lambdaQuery(MeetingExpert.class) + .select(MeetingExpert::getExpertId) + .in(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode(), ExpertAttendStatus.NOTICING.getCode()) + .in(MeetingExpert::getMeetingId, meetingIds); + List meetingExperts = meetingExpertService.list(meetingExpertQuery); + return CollUtils.fieldList(meetingExperts, MeetingExpert::getExpertId); + } + + public Set listExpertLeaveOrInvited(LocalDateTime start, LocalDateTime end) { + Set notInUserIds = new HashSet<>(); + notInUserIds.addAll(listInvitedExpertByTime(start, end)); + return notInUserIds; + } + + public Set getAvoidExpert(List appoints, AvoidInfoDTO avoid, LocalDateTime start, LocalDateTime end) { + Set 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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java new file mode 100644 index 0000000..b221be6 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java @@ -0,0 +1,222 @@ +package com.ningdatech.pmapi.meeting.helper; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ningdatech.basic.exception.BizException; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.pmapi.common.util.BizUtils; +import com.ningdatech.pmapi.common.util.StrUtils; +import com.ningdatech.pmapi.expert.constant.ExpertAccountStatusEnum; +import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; +import com.ningdatech.pmapi.expert.model.dto.ExpertDictionaryDTO; +import com.ningdatech.pmapi.expert.model.dto.ExpertUserFullInfoDTO; +import com.ningdatech.pmapi.expert.service.ExpertInfoService; +import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO; +import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus; +import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq; +import com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVO; +import com.ningdatech.pmapi.meeting.entity.vo.MeetingByManagerVO; +import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService; +import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import com.ningdatech.pmapi.meta.constant.DictExpertInfoTypeEnum; +import com.ningdatech.pmapi.meta.helper.DictionaryCache; +import com.ningdatech.pmapi.meta.model.dto.DictionaryDTO; +import lombok.AllArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + *

+ * MeetingManageHelper + *

+ * + * @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 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 + * @author WendyYang + **/ + public Map getExpertBasicInfo(List userIds) { + /*List 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 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 expertIds) { + ExpertInviteAvoidRule avoidRule = inviteAvoidRuleService.getByMeetingId(meetingId); + avoidRule.setExpertIds(CollUtils.joinByComma(expertIds)); + inviteAvoidRuleService.saveOrUpdate(avoidRule); + } + + /** + * 校验是否能够进行指定邀请 + * + * @param meetingId 会议ID + * @param expertIds 专家ID + * @author WendyYang + **/ + public List appointExpertCheck(Long meetingId, List expertIds) { + List experts = expertUserFullInfoService.listByUserId(expertIds); + AvoidInfoDTO avoidRule = getAvoidInfoDto(meetingId); + Map 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 expertMap = CollUtils.listToMap(experts, ExpertUserFullInfo::getUserId); + List meetingExperts = meetingExpertService.listByMeetingId(meetingId); + if (!meetingExperts.isEmpty()) { + Comparator comparator = Comparator.comparing(MeetingExpert::getUpdateOn).reversed(); + Collection 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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java new file mode 100644 index 0000000..bd713f0 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java @@ -0,0 +1,50 @@ +package com.ningdatech.pmapi.meeting.helper; + +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + +/** + *

+ * YxtCallOrSmsHelper + *

+ * + * @author WendyYang + * @since 23:59 2022/8/31 + */ +@Component +@AllArgsConstructor +public class YxtCallOrSmsHelper { + + // private final YinXinTongClient yinXinTongClient; + + public void callByMeetingExperts(Meeting meeting, List experts) { + /*String callContent = String.format(YxtCallTemplateConst.OFFLINE_TEMPLATE, + meeting.getHoldCompany(), meeting.getName(), + meeting.getStartTime().format(DateUtil.DTF_YMD_HM), meeting.getRegionDetail()); + List 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 smsList) { + yinXinTongClient.submitSmsTask(new SendSmsCmd() {{ + setContextList(smsList); + setSmsSignEnum(YxtSmsSignEnum.ZJS_ELECTRONIC_EXPERT_LIB); + }}); + } + + public void sendSms(SendSmsContext sms) { + sendSms(Collections.singletonList(sms)); + }*/ + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/DashboardManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/DashboardManage.java new file mode 100644 index 0000000..0d1a634 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/DashboardManage.java @@ -0,0 +1,241 @@ +package com.ningdatech.pmapi.meeting.manage; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ningdatech.basic.exception.BizException; +import com.ningdatech.basic.model.PagePo; +import com.ningdatech.basic.model.PageVo; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO; +import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard; +import com.ningdatech.pmapi.meeting.entity.req.MeetingCalenderReq; +import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq; +import com.ningdatech.pmapi.meeting.entity.vo.*; +import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import com.ningdatech.pmapi.meeting.service.IMeetingService; +import com.ningdatech.pmapi.meta.helper.DictionaryCache; +import com.ningdatech.pmapi.user.util.LoginUserUtil; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * DashboardManage + *

+ * + * @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 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 meetingPage = meetingManage.meetingListByExpert(meetingListPo); + Map> meetingByDate = meetingPage.getRecords().stream().map(w -> { + List> 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 result = new ArrayList<>(); + while (!tempDate.isAfter(po.getEndDate())) { + MeetingCalenderItemVO item = new MeetingCalenderItemVO(); + List 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 expertEvaluationToDo(PagePo po) { + PageVo result = PageVo.of(new ArrayList<>(), 0L); + // 查询所有未完成的项目 + LambdaQueryWrapper query = Wrappers.lambdaQuery(Meeting.class) + .select(Meeting::getId) + .ne(Meeting::getStatus, MeetingStatus.Manager.CANCELED.getCode()) + .eq(Meeting::getCreateBy, LoginUserUtil.getUserId()); + List meetings = meetingService.list(query); + if (meetings.isEmpty()) { + return PageVo.empty(); + } + List meetingIds = CollUtils.fieldList(meetings, Meeting::getId); + Page page = meetingExpertEvaluationService.pageExpertEvaluationTodo(meetingIds, po); + if (page.getTotal() > 0) { + List expertIds = new ArrayList<>(), meetingIdsByPage = new ArrayList<>(); + page.getRecords().forEach(w -> { + meetingIdsByPage.add(w.getMeetingId()); + expertIds.add(w.getExpertId()); + }); + Map meetingMap = CollUtils.listToMap(meetingService.listByIds(meetingIdsByPage), Meeting::getId); + Map 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 expertConfirmToDo(PagePo po) { + // 查询所有未完成的项目 + LambdaQueryWrapper query = Wrappers.lambdaQuery(Meeting.class) + .eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode()) + .eq(Meeting::getCreateBy, LoginUserUtil.getUserId()) + .orderByDesc(Meeting::getStartTime); + List meetings = meetingService.list(query); + if (meetings.isEmpty()) { + return PageVo.empty(); + } + List meetingIds = CollUtils.fieldList(meetings, Meeting::getId); + Map 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 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 expertReplaceTodoList(PagePo po) { + // 查询所有未完成的项目 + LambdaQueryWrapper query = Wrappers.lambdaQuery(Meeting.class) + .eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode()) + .eq(Meeting::getCreateBy, LoginUserUtil.getUserId()) + .orderByDesc(Meeting::getStartTime); + List meetings = meetingService.list(query); + if (meetings.isEmpty()) { + return PageVo.empty(); + } + List meetingIds = CollUtils.fieldList(meetings, Meeting::getId); + Page page = meetingExpertService.pageExpertByStatusAndMeetingIds(new Page<>(po.getPageNumber(), po.getPageSize()), + meetingIds, ExpertAttendStatus.ON_LEAVE); + if (page.getTotal() == 0) { + return PageVo.empty(); + } + Map meetingMap = CollUtils.listToMap(meetings, Meeting::getId); + List expertIds = CollUtils.fieldList(page.getRecords(), MeetingExpert::getExpertId); + Map basicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds); + List 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 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 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; + } + + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java new file mode 100644 index 0000000..546b676 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/ExpertInviteManage.java @@ -0,0 +1,672 @@ +package com.ningdatech.pmapi.meeting.manage; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.pmapi.common.util.BizUtils; +import com.ningdatech.pmapi.expert.constant.ExpertAccountStatusEnum; +import com.ningdatech.pmapi.expert.entity.ExpertAvoidCompany; +import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; +import com.ningdatech.pmapi.expert.service.IExpertAvoidCompanyService; +import com.ningdatech.pmapi.expert.service.IExpertIntentionWorkRegionService; +import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService; +import com.ningdatech.pmapi.meeting.builder.ExpertInviteBuilder; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.*; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper; +import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper; +import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import com.ningdatech.pmapi.meeting.service.IMeetingService; +import com.ningdatech.pmapi.meta.helper.TagCache; +import com.ningdatech.pmapi.meta.model.entity.ExpertDictionary; +import com.ningdatech.pmapi.meta.model.entity.ExpertTag; +import com.ningdatech.pmapi.meta.service.IExpertDictionaryService; +import com.ningdatech.pmapi.meta.service.IExpertTagService; +import lombok.AllArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.collections4.Predicate; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.RandomUtils; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + *

+ * 专家邀请管理 + *

+ * + * @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> COLL_EMPTY = (coll) -> coll != null && coll.isEmpty(); + + private LambdaQueryWrapper 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 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 + * @author WendyYang + **/ + private List expertIdsByRegion(RandomInviteRuleDTO rule) { + if (rule.getIntentionRegionCode() == null || rule.getIntentionRegionLevel() == null) { + return null; + } + return workRegionService.userIdsMatchIntentionRegion(rule.getIntentionRegionCode(), rule.getIntentionRegionLevel()); + } + + + /** + * 如果抽取回避单位和专家回避单位一致,同样需要回避该专家 + * + * @param companyNames / + * @return / + */ + private List avoidCompanyExpertIds(List companyNames) { + if (CollUtil.isEmpty(companyNames)) { + return new ArrayList<>(); + } + List 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 expertAvoidCompanyList = iExpertAvoidCompanyService.list(Wrappers.lambdaQuery(ExpertAvoidCompany.class) + .in(ExpertAvoidCompany::getCompanyName, dealCompanyNames)); + return expertAvoidCompanyList.stream().map(ExpertAvoidCompany::getUserId).distinct().collect(Collectors.toList()); + } + + private List mergeExpertIdsByCondition(RandomInviteRuleDTO rule, AvoidInfoDTO avoidInfo) { + // 处理履职意向地 + List expertIdsByIntentionRegion = expertIdsByRegion(rule); + if (COLL_EMPTY.evaluate(expertIdsByIntentionRegion)) { + return null; + } + // 处理专家标签 + List expertIdsByTag = expertIdsByTag(rule); + if (COLL_EMPTY.evaluate(expertIdsByTag)) { + return null; + } + // 处理专家字典 + List expertIdsByDict = expertIdsByDict(rule); + if (COLL_EMPTY.evaluate(expertIdsByDict)) { + return null; + } + // 处理专家回避单位 与 抽取回避单位是否相同 + List avoidCompanyExpertIds = avoidCompanyExpertIds(avoidInfo.getCompanyIds()); + + // 聚合用户ID + List 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 + * @author WendyYang + **/ + private List expertIdsByTag(RandomInviteRuleDTO rule) { + if (CollectionUtils.isEmpty(rule.getExpertTags())) { + return null; + } + LambdaQueryWrapper query = Wrappers.lambdaQuery(ExpertTag.class) + .select(ExpertTag::getUserId, ExpertTag::getExpertInfoField); + rule.getExpertTags().forEach(tag -> { + List 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 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 + * @author WendyYang + **/ + private List expertIdsByDict(RandomInviteRuleDTO rule) { + if (CollectionUtils.isEmpty(rule.getExpertDicts())) { + return null; + } + LambdaQueryWrapper 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 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 appointExpertIds, + LocalDateTime start, + LocalDateTime end) { + ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0); + List 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 tmpAvoidCompany = new HashSet<>(); + if (avoidCompany) { + List 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 appointUsers = expertUserFullInfoService.listByUserId(appointExpertIds); + tmpAvoidCompany.addAll(CollUtils.fieldList(appointUsers, ExpertUserFullInfo::getCompany)); + } + LambdaQueryWrapper 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 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 tempExperts = expertInviteHelper.getAvoidExpert(appointExpertIds, avoidInfo, start, end); + query.notIn(ExpertUserFullInfo::getUserId, tempExperts); + } else { + Set notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end); + if (!notInUserIds.isEmpty()) { + query.notIn(ExpertUserFullInfo::getUserId, notInUserIds); + } + } + List userFullInfos = expertUserFullInfoService.list(query); + if (userFullInfos.isEmpty()) { + return result; + } + if (avoid && avoidInfo.getAvoidMates()) { + // 回避同单位其他专家 + Map> userFullInfoMap = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompany); + result.setTotal(userFullInfoMap.size()); + if (randomRule.getCount() == null || userFullInfoMap.size() >= randomRule.getCount()) { + List userFullInfoList = inviteGroupByCompany(userFullInfoMap, randomRule.getCount()); + result.setExperts(userFullInfoList); + } + } else { + result.setTotal(userFullInfos.size()); + // count为空表示数量校验 + if (randomRule.getCount() == null || result.getTotal() >= randomRule.getCount()) { + List 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 meetingExperts, + Integer count, + LocalDateTime start, + LocalDateTime end, + Long replacedExpertId) { + ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0); + // 合并标签、字典 + List expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidInfo); + if (expertIdsIn == null) { + return result; + } + LambdaQueryWrapper 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 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 notInExpertIds = expertInviteHelper.getAvoidExpert(avoidInfo.getExpertIds(), null, start, end); + query.notIn(ExpertUserFullInfo::getUserId, notInExpertIds); + } else { + Set notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end); + if (notInUserIds.size() > 0) { + query.notIn(ExpertUserFullInfo::getUserId, notInUserIds); + } + } + List userFullInfos = expertUserFullInfoService.list(query); + if (userFullInfos.size() == 0) { + return result; + } + Comparator sort = Comparator.comparing(MeetingExpert::getUpdateOn).reversed(); + Map tempExpertIdsMap = BizUtils.groupFirstMap(meetingExperts, MeetingExpert::getExpertId, sort); + Map> expertIdGroupByStatus = tempExpertIdsMap.values().stream() + .collect(Collectors.groupingBy(w -> ExpertAttendStatus.getByCode(w.getStatus()))); + // 回避同单位其他专家 + List removeExpertByCompany = new ArrayList<>(); + BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.AGREED), removeExpertByCompany::addAll); + BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.NOTICING), removeExpertByCompany::addAll); + List removeExpertIds = new ArrayList<>(); + // 拒绝参加的不可以被再次抽中 + BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.REFUSED), w -> { + List tempRefused = CollUtils.fieldList(w, MeetingExpert::getExpertId); + removeExpertIds.addAll(tempRefused); + }); + // 被取消的也不可以被再次抽中 + BizUtils.notEmpty(expertIdGroupByStatus.get(ExpertAttendStatus.CANCELED), w -> { + List 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 tempExpertIds = CollUtils.fieldList(removeExpertByCompany, MeetingExpert::getExpertId); + if (avoidInfo.getAvoidMates()) { + if (!tempExpertIds.isEmpty()) { + Set 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> 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> selectMeetingExpertByCount() { + LambdaQueryWrapper query = Wrappers.lambdaQuery(Meeting.class) + .select(Meeting::getId) + .orderByDesc(Meeting::getCreateOn) + .last("limit " + 5); + List meetingIds = CollUtils.fieldList(meetingService.list(query), Meeting::getId); + if (meetingIds.isEmpty()) { + return Collections.emptyList(); + } + Map> 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 + * @author WendyYang + **/ + private List inviteGroupByCompany(Map> expertGroupByCompany, Integer count) { + if (MapUtils.isEmpty(expertGroupByCompany)) { + return Collections.emptyList(); + } + List> 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 result = new ArrayList<>(); + List keySet = new ArrayList<>(expertGroupByCompany.keySet()); + for (int i = 0; i < count; i++) { + String key = keySet.get(RandomUtils.nextInt(0, keySet.size())); + List expertUserFullInfos = expertGroupByCompany.get(key); + for (List expertList : meetingExperts) { + List 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 inviteWithoutCompany(List userFullInfos, Integer count) { + List result = new ArrayList<>(); + List> 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 meetingExperts : meetingExpertList) { + List 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 checkAppointExpertConflictWithAvoidRule(AppointInviteRuleDTO appointRule, AvoidInfoDTO avoidRule) { + if (appointRule == null) { + return Collections.emptyMap(); + } + List expertInfos = expertUserFullInfoService.listByUserId(appointRule.getExpertIds()); + Map infoMap = CollUtils.listToMap(expertInfos, ExpertUserFullInfo::getUserId); + if (avoidRule == null) { + return infoMap; + } + Map 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 randomRules, + AppointInviteRuleDTO appointRule, + AvoidInfoDTO avoid) { + Map appointUser = checkAppointExpertConflictWithAvoidRule(appointRule, avoid); + List expertInserts = new ArrayList<>(); + boolean appointed = appointRule != null; + // 处理随机抽取规则 + if (CollectionUtils.isNotEmpty(randomRules)) { + List randoms = new ArrayList<>(); + List expertsByRandom = new ArrayList<>(); + List 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); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingExpertManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingExpertManage.java new file mode 100644 index 0000000..b297101 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingExpertManage.java @@ -0,0 +1,25 @@ +package com.ningdatech.pmapi.meeting.manage; + +import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; + +/** + *

+ * MeetingExpertManage + *

+ * + * @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; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java new file mode 100644 index 0000000..b7df186 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java @@ -0,0 +1,708 @@ +package com.ningdatech.pmapi.meeting.manage; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ningdatech.basic.exception.BizException; +import com.ningdatech.basic.model.IdVo; +import com.ningdatech.basic.model.PageVo; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.basic.util.ValidUtil; +import com.ningdatech.cache.lock.DistributedLock; +import com.ningdatech.file.entity.vo.result.AttachFileVo; +import com.ningdatech.file.service.FileService; +import com.ningdatech.pmapi.common.util.BizUtils; +import com.ningdatech.pmapi.common.util.StrUtils; +import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; +import com.ningdatech.pmapi.expert.helper.PermissionCheckHelper; +import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService; +import com.ningdatech.pmapi.meeting.builder.ExpertInviteBuilder; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.*; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus.Manager; +import com.ningdatech.pmapi.meeting.entity.req.*; +import com.ningdatech.pmapi.meeting.entity.vo.*; +import com.ningdatech.pmapi.meeting.entity.vo.ExpertInviteDetailVO.ExpertAttendListItemVO; +import com.ningdatech.pmapi.meeting.entity.vo.ExpertInviteDetailVO.RandomInviteListItemVO; +import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper; +import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper; +import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper; +import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService; +import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import com.ningdatech.pmapi.meeting.service.IMeetingService; +import com.ningdatech.pmapi.meeting.task.ExpertInviteTask; +import com.ningdatech.pmapi.meta.helper.DictionaryCache; +import com.ningdatech.pmapi.meta.helper.TagCache; +import com.ningdatech.pmapi.user.entity.UserInfo; +import com.ningdatech.pmapi.user.service.IUserInfoService; +import com.ningdatech.pmapi.user.util.LoginUserUtil; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.function.Function; + +/** + *

+ * MeetingManage + *

+ * + * @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 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 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 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 meetingListByExpert(MeetingListReq req) { + Long expertId = req.getExpertId() != null ? req.getExpertId() : LoginUserUtil.getUserId(); + List expertDtoList = meetingExpertService.listByExpertIdAndStatus(expertId, req.getStatus(), null); + if (expertDtoList.isEmpty()) { + return PageVo.empty(); + } + Map mapByMeetingId = new HashMap<>(16); + expertDtoList.forEach(w -> mapByMeetingId.put(w.getMeetingId(), w)); + LambdaQueryWrapper query = new LambdaQueryWrapper() + .orderByDesc(Meeting::getCreateOn) + .in(Meeting::getId, mapByMeetingId.keySet()) + .ne(Meeting::getStatus, Manager.CANCELED.getCode()); + if (req.getExpertId() == null) { + meetingManageHelper.buildMeetingQuery(query, req); + } + Page page = meetingService.page(req.page(), query); + if (page.getTotal() == 0) { + return PageVo.empty(); + } + PageVo 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 meetingListByManager(MeetingListReq po) { + LambdaQueryWrapper query = new LambdaQueryWrapper() + .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 page = meetingService.page(po.page(), query); + if (page.getTotal() == 0) { + return PageVo.empty(); + } + PageVo result = new PageVo<>(new ArrayList<>(), page.getTotal()); + Map countConfirmMap = new HashMap<>(16); + List 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 attachments = new ArrayList<>(); + if (StrUtils.isNotBlank(attachment)) { + attachments.addAll(fileService.getByIds(BizUtils.splitToLong(attachment))); + } + Integer attendStatus = null; + if (LoginUserUtil.isExpert()) { + List meIds = Collections.singletonList(meetingId); + List 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 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 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 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 experts = meetingExpertService.listByMeetingId(meetingId); + if (experts.isEmpty()) { + return result; + } + List randomList = new ArrayList<>(); + List appointList = new ArrayList<>(); + List attendList = new ArrayList<>(); + List 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 expertBasicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds); + Function 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 meList = meetingExpertService.listExpertByAgreeAttend(Collections.singletonList(po.getId())); + if (!meList.isEmpty() && old.getStartTime().isAfter(now)) { + // TODO + /*String meetingType = dictionaryCache.getByCode(old.getType()).getName(); + List 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 inviteRules = inviteRuleService.listByMeetingId(meetingId); + if (!result.setAndGetInvited(inviteRules.size() > 0)) { + return result; + } + result.setRandomRules(new ArrayList<>()); + Map> groupByType = CollUtils.group(inviteRules, w -> ExpertInviteType.getByCode(w.getInviteType())); + List 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 tagNames = CollUtils.convert(w.getTagCodes(), tagCode -> tagCache.getByTagCode(tagCode).getTagName()); + w.setTagCodes(tagNames); + }); + } + if (randomRule.getExpertDicts() != null) { + randomRule.getExpertDicts().forEach(w -> { + List dictNames = CollUtils.convert(w.getDictCodes(), dictCode -> dictionaryCache.getByCode(dictCode).getName()); + w.setDictCodes(dictNames); + }); + } + if (StrUtil.isNotEmpty(randomRule.getIntentionRegionCode())) { + /*List intentionRegions = regionCache.listParents(randomRule.getIntentionRegionCode(), randomRule.getIntentionRegionLevel()); + randomRule.setIntentionRegions(intentionRegions);*/ + } + if (StrUtil.isNotEmpty(randomRule.getExpertRegionCode())) { + /*List expertRegions = regionCache.listParents(randomRule.getExpertRegionCode(), randomRule.getExpertRegionLevel()); + randomRule.setExpertRegions(expertRegions);*/ + } + result.getRandomRules().add(randomRule); + }); + } + List 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 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 userInfos = meetingManageHelper.appointExpertCheck(po.getMeetingId(), Collections.singletonList(po.getExpertId())); + expertFullInfo = userInfos.get(0); + } else { + List 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 expertIds = avoidInfoDto.getExpertIds(); + if (CollUtil.isEmpty(expertIds)) { + expertIds = new ArrayList<>(); + } + expertIds.add(meetingExpert.getExpertId()); + avoidInfoDto.setExpertIds(expertIds); + meetingManageHelper.saveAvoidInfo(po.getMeetingId(), avoidInfoDto.getExpertIds()); + + List 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 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 userInfos = meetingManageHelper.appointExpertCheck(po.getMeetingId(), po.getExpertIds()); + List 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 update = Wrappers.lambdaUpdate(Meeting.class) + .set(Meeting::getSendMeetingNotice, Boolean.TRUE) + .eq(Meeting::getId, meetingId); + meetingService.update(update); + // 发送会议通知 + List experts = meetingExpertService.listExpertByAgreeAttend(Collections.singletonList(meetingId)); + if (!experts.isEmpty()) { + // TODO + // String meetingType = dictionaryCache.getByCode(meeting.getType()).getName(); + // List 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 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 experts = meetingExpertService.listExpertByAgreeAttend(Collections.singletonList(po.getMeetingId())); + if (!experts.isEmpty()) { + // TODO + // meeting.setCancelRemark(po.getCancelRemark()); + // String meetingType = dictionaryCache.getByCode(meeting.getType()).getName(); + // List 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 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()); + } + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.java new file mode 100644 index 0000000..6fb3866 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.java @@ -0,0 +1,16 @@ +package com.ningdatech.pmapi.meeting.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule; + +/** + *

+ * Mapper 接口 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +public interface ExpertInviteAvoidRuleMapper extends BaseMapper { + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.xml b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.xml new file mode 100644 index 0000000..791c86d --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteAvoidRuleMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.java new file mode 100644 index 0000000..883a6b9 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.java @@ -0,0 +1,16 @@ +package com.ningdatech.pmapi.meeting.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; + +/** + *

+ * Mapper 接口 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +public interface ExpertInviteRuleMapper extends BaseMapper { + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.xml b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.xml new file mode 100644 index 0000000..718fae3 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/ExpertInviteRuleMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.java new file mode 100644 index 0000000..7c68292 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.java @@ -0,0 +1,26 @@ +package com.ningdatech.pmapi.meeting.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpertEvaluation; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; + +/** + *

+ * 专家评价表 Mapper 接口 + *

+ * + * @author WendyYang + * @since 2022-07-25 + */ +public interface MeetingExpertEvaluationMapper extends BaseMapper { + + Page> countExpertAttend(@Param("expertIds") Collection expertIds, @Param("isAttended") Boolean isAttended, Page> page); + + Page pageExpertEvaluationToDo(@Param("meetingIds") Collection meetingIds, Page po); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.xml b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.xml new file mode 100644 index 0000000..3f30b52 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertEvaluationMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.java new file mode 100644 index 0000000..f360ac6 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.java @@ -0,0 +1,81 @@ +package com.ningdatech.pmapi.meeting.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** + *

+ * 事务专家表 Mapper 接口 + *

+ * + * @author WendyYang + * @since 2022-07-27 + */ +public interface MeetingExpertMapper extends BaseMapper { + + /** + * 根据专家状态查询需要参加的会议信息 + * + * @param expertId 专家ID + * @param status 状态 + * @param meetingIds 会议ID + * @return 专家参会状态列表 + * @author WendyYang + **/ + List selectByExpertIdAndStatus(@Param("expertId") Long expertId, + @Param("status") Integer status, + @Param("meetingIds") List meetingIds); + + /** + * 分页查询专家列表 + * + * @param page 分页数据 + * @param status 状态{@link ExpertAttendStatus} + * @param meetingId 会议ID + * @param inviteType 邀请类型 + * @return Page + * @author WendyYang + **/ + Page selectExpertByStatusAndMeetingId(Page page, @Param("status") Integer status, + @Param("meetingId") Long meetingId, + @Param("inviteType") Integer inviteType); + + /** + * 分页查询专家列表 + * + * @param page 分页数据 + * @param status 状态{@link ExpertAttendStatus} + * @param meetingIds 会议ID + * @return Page + * @author WendyYang + **/ + Page selectExpertByStatusAndMeetingIds(Page page, @Param("status") Integer status, + @Param("meetingIds") Collection 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 listExpertLastByMeetingIds(@Param("meetingIds") Collection meetingIds); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.xml b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.xml new file mode 100644 index 0000000..fedb7ff --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingExpertMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.java new file mode 100644 index 0000000..aa37b01 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.java @@ -0,0 +1,21 @@ +package com.ningdatech.pmapi.meeting.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; + +import java.util.List; + +/** + *

+ * 会议 Mapper 接口 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +public interface MeetingMapper extends BaseMapper { + + List> meetingCountSummary(Long createBy); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.xml b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.xml new file mode 100644 index 0000000..bc6b0d8 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/mapper/MeetingMapper.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java new file mode 100644 index 0000000..24bb363 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteAvoidRuleService.java @@ -0,0 +1,28 @@ +package com.ningdatech.pmapi.meeting.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule; +import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO; + +/** + *

+ * IExpertInviteAvoidRuleService服务类 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +public interface IExpertInviteAvoidRuleService extends IService { + + ExpertInviteAvoidRule getByMeetingId(Long meetingId); + + /** + * 获取回避信息 + * + * @param meetingId 会议ID + * @return com.ningdatech.emapi.meeting.entity.dto.AvoidInfoDto + * @author WendyYang + **/ + AvoidInfoDTO getAvoidInfoDto(Long meetingId); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteRuleService.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteRuleService.java new file mode 100644 index 0000000..94502ec --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IExpertInviteRuleService.java @@ -0,0 +1,24 @@ +package com.ningdatech.pmapi.meeting.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务类 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +public interface IExpertInviteRuleService extends IService { + + List listByMeetingId(Long meetingId); + + Map randomRuleByMeetingId(Long meetingId); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertEvaluationService.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertEvaluationService.java new file mode 100644 index 0000000..d4fe30a --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertEvaluationService.java @@ -0,0 +1,26 @@ +package com.ningdatech.pmapi.meeting.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ningdatech.basic.model.PagePo; +import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpertEvaluation; + +import java.util.Collection; + +/** + *

+ * 专家评价表 服务类 + *

+ * + * @author WendyYang + * @since 2022-07-25 + */ +public interface IMeetingExpertEvaluationService extends IService { + + Page> listExpertAttendSummary(Collection expertIds, Boolean isAttended, Page> page); + + Page pageExpertEvaluationTodo(Collection meetingIds, PagePo po); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java new file mode 100644 index 0000000..00a7237 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingExpertService.java @@ -0,0 +1,123 @@ +package com.ningdatech.pmapi.meeting.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO; +import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + *

+ * 事务专家表 服务类 + *

+ * + * @author WendyYang + * @since 2022-07-27 + */ +public interface IMeetingExpertService extends IService { + + /** + * 查询专家的参与状态 + * + * @param expertId 专家ID + * @param status 状态{@link MeetingStatus.Expert} + * @param meetingIds 会议ID + * @return 会议参加状态统计 + * @author WendyYang + **/ + List listByExpertIdAndStatus(Long expertId, Integer status, List meetingIds); + + /** + * 查询每个事物的确认进度 + * + * @param meetingIds 事务ID + * @return 确认进度 + * @author WendyYang + **/ + Map countConfirmedByMeetingIds(List meetingIds); + + /** + * 根据事务ID查询所有邀请记录 + * + * @param meetingId 事务ID + * @return 邀请专家记录 + */ + List listByMeetingId(Long meetingId); + + /** + * 根据事务ID查询所有邀请记录 + * + * @param meetingIds 事务ID + * @return List + */ + List listByMeetingIds(List 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 + * @author WendyYang + **/ + Page pageExpertByStatusAndMeetingId(Page page, Long meetingId, ExpertAttendStatus status, Integer inviteType); + + /** + * 批量查询某个状态的专家邀请记录 + * + * @param page 分页参数 + * @param meetingIds 会议ID + * @param status 状态 + * @return 专家邀请记录 + * @author WendyYang + **/ + Page pageExpertByStatusAndMeetingIds(Page page, List 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 + * @author WendyYang + **/ + List listExpertByAgreeAttend(Collection meetingIds); + + /** + * 查询会议的所有被抽取人最后一条记录 + * + * @param meetingIds 会议ID + * @return 抽取记录 + * @author WendyYang + **/ + List listExpertLastByMeetingIds(Collection meetingIds); + +} \ No newline at end of file diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingService.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingService.java new file mode 100644 index 0000000..83ad9e5 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/IMeetingService.java @@ -0,0 +1,28 @@ +package com.ningdatech.pmapi.meeting.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard; + +import java.util.Map; + +/** + *

+ * 会议 服务类 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +public interface IMeetingService extends IService { + + /** + * 停止抽取 + * + * @param meetingId 会议ID + **/ + void stopRandomInvite(Long meetingId); + + Map meetingCountSummary(Long createBy); + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java new file mode 100644 index 0000000..1a02396 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteAvoidRuleServiceImpl.java @@ -0,0 +1,45 @@ +package com.ningdatech.pmapi.meeting.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ningdatech.pmapi.common.util.BizUtils; +import com.ningdatech.pmapi.common.util.StrUtils; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule; +import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO; +import com.ningdatech.pmapi.meeting.mapper.ExpertInviteAvoidRuleMapper; +import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +@Service +public class ExpertInviteAvoidRuleServiceImpl extends ServiceImpl implements IExpertInviteAvoidRuleService { + + @Override + public ExpertInviteAvoidRule getByMeetingId(Long meetingId) { + LambdaQueryWrapper 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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteRuleServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteRuleServiceImpl.java new file mode 100644 index 0000000..ddbf3a4 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/ExpertInviteRuleServiceImpl.java @@ -0,0 +1,47 @@ +package com.ningdatech.pmapi.meeting.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +import com.ningdatech.pmapi.meeting.mapper.ExpertInviteRuleMapper; +import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 服务实现类 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +@Service +public class ExpertInviteRuleServiceImpl extends ServiceImpl implements IExpertInviteRuleService { + + public static LambdaQueryWrapper wrapperByMeetingId(Long meetingId) { + return Wrappers.lambdaQuery(ExpertInviteRule.class).eq(ExpertInviteRule::getMeetingId, meetingId); + } + + @Override + public List listByMeetingId(Long meetingId) { + return list(wrapperByMeetingId(meetingId)); + } + + @Override + public Map randomRuleByMeetingId(Long meetingId) { + LambdaQueryWrapper query = wrapperByMeetingId(meetingId) + .eq(ExpertInviteRule::getInviteType, ExpertInviteType.RANDOM.getCode()); + List inviteRules = baseMapper.selectList(query); + return inviteRules.stream().collect(Collectors.toMap(ExpertInviteRule::getId, + w -> JSONObject.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class))); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertEvaluationServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertEvaluationServiceImpl.java new file mode 100644 index 0000000..a668442 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertEvaluationServiceImpl.java @@ -0,0 +1,36 @@ +package com.ningdatech.pmapi.meeting.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ningdatech.basic.model.PagePo; +import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpertEvaluation; +import com.ningdatech.pmapi.meeting.mapper.MeetingExpertEvaluationMapper; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService; +import org.springframework.stereotype.Service; + +import java.util.Collection; + +/** + *

+ * 专家评价表 服务实现类 + *

+ * + * @author WendyYang + * @since 2022-07-25 + */ +@Service +public class MeetingExpertEvaluationServiceImpl extends ServiceImpl implements IMeetingExpertEvaluationService { + + @Override + public Page> listExpertAttendSummary(Collection expertIds, Boolean isAttended, Page> page) { + return baseMapper.countExpertAttend(expertIds, isAttended, page); + } + + @Override + public Page pageExpertEvaluationTodo(Collection meetingIds, PagePo po) { + return baseMapper.pageExpertEvaluationToDo(meetingIds, new Page<>(po.getPageNumber(), po.getPageSize())); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertServiceImpl.java new file mode 100644 index 0000000..e953c75 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingExpertServiceImpl.java @@ -0,0 +1,135 @@ +package com.ningdatech.pmapi.meeting.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO; +import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +import com.ningdatech.pmapi.meeting.mapper.ExpertInviteRuleMapper; +import com.ningdatech.pmapi.meeting.mapper.MeetingExpertMapper; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 事务专家表 服务实现类 + *

+ * + * @author WendyYang + * @since 2022-07-27 + */ +@Service +@RequiredArgsConstructor +public class MeetingExpertServiceImpl extends ServiceImpl implements IMeetingExpertService { + + private final ExpertInviteRuleMapper inviteRuleMapper; + + @Override + public List listByExpertIdAndStatus(Long expertId, Integer status, List meetingIds) { + return baseMapper.selectByExpertIdAndStatus(expertId, status, meetingIds); + } + + @Override + public Map countConfirmedByMeetingIds(List meetingIds) { + List inviteRules = inviteRuleMapper.selectList(Wrappers.lambdaQuery(ExpertInviteRule.class) + .eq(ExpertInviteRule::getInviteType, ExpertInviteType.RANDOM.getCode()) + .in(ExpertInviteRule::getMeetingId, meetingIds)); + Map countByRandom = CollUtils.groupSumInt(inviteRules, ExpertInviteRule::getMeetingId, ExpertInviteRule::getInviteCount); + Page 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 listByMeetingId(Long meetingId) { + LambdaQueryWrapper query = Wrappers.lambdaQuery(MeetingExpert.class) + .eq(MeetingExpert::getMeetingId, meetingId) + .orderByDesc(MeetingExpert::getUpdateOn); + return list(query); + } + + @Override + public List listByMeetingIds(List meetingIds) { + LambdaQueryWrapper query = Wrappers.lambdaQuery(MeetingExpert.class) + .in(MeetingExpert::getMeetingId, meetingIds) + .orderByDesc(MeetingExpert::getUpdateOn); + return list(query); + } + + @Override + public MeetingExpert getByMeetingIdAndExpertId(Long meetingId, Long expertId) { + LambdaQueryWrapper 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 pageExpertByStatusAndMeetingId(Page page, Long meetingId, ExpertAttendStatus status, Integer inviteType) { + return baseMapper.selectExpertByStatusAndMeetingId(page, status.getCode(), meetingId, inviteType); + } + + @Override + public Page pageExpertByStatusAndMeetingIds(Page page, List 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 listExpertByAgreeAttend(Collection meetingIds) { + LambdaQueryWrapper query = Wrappers.lambdaQuery(MeetingExpert.class) + .eq(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode()) + .in(MeetingExpert::getMeetingId, meetingIds); + return baseMapper.selectList(query); + } + + @Override + public List listExpertLastByMeetingIds(Collection meetingIds) { + return baseMapper.listExpertLastByMeetingIds(meetingIds); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingServiceImpl.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingServiceImpl.java new file mode 100644 index 0000000..fbcdc1d --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/service/impl/MeetingServiceImpl.java @@ -0,0 +1,42 @@ +package com.ningdatech.pmapi.meeting.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.pmapi.common.model.entity.CountGroupByDTO; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard; +import com.ningdatech.pmapi.meeting.mapper.MeetingMapper; +import com.ningdatech.pmapi.meeting.service.IMeetingService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 会议 服务实现类 + *

+ * + * @author WendyYang + * @since 2022-07-26 + */ +@Service +public class MeetingServiceImpl extends ServiceImpl 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 meetingCountSummary(Long createBy) { + List> meetingCountSummary = baseMapper.meetingCountSummary(createBy); + return CollUtils.listToMap(meetingCountSummary, + w -> MeetingStatusByDashboard.valueOf(w.getGroupKey()), + CountGroupByDTO::getTotal); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java new file mode 100644 index 0000000..8f971b9 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java @@ -0,0 +1,241 @@ +//package com.ningdatech.pmapi.meeting.task; +// +//import com.alibaba.fastjson.JSON; +//import com.alibaba.fastjson.JSONObject; +//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +//import com.baomidou.mybatisplus.core.toolkit.Wrappers; +//import com.ningdatech.basic.util.StrPool; +//import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo; +//import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService; +//import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +//import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +//import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +//import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO; +//import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper; +//import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +//import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +//import com.ningdatech.pmapi.meeting.service.IMeetingService; +//import com.ningdatech.pmapi.meta.helper.DictionaryCache; +//import com.ningdatech.pmapi.sms.utils.DateUtil; +//import lombok.AllArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +//import org.springframework.stereotype.Component; +// +//import javax.annotation.PostConstruct; +//import java.time.Duration; +//import java.time.Instant; +//import java.time.LocalDateTime; +//import java.time.temporal.ChronoUnit; +//import java.util.*; +//import java.util.stream.Collectors; +// +//import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus.AGREED; +//import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus.NOTICING; +// +// +///** +// *

+// * ExpertCallResultRewriteTask +// *
+// * 专家电话结果回填 +// *

+// * +// * @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 meQuery = Wrappers.lambdaQuery(MeetingExpert.class) +// .eq(MeetingExpert::getStatus, NOTICING.getCode()) +// .eq(MeetingExpert::getInviteType, ExpertInviteType.RANDOM.getCode()); +// List experts = meetingExpertService.list(meQuery); +// if (experts.isEmpty()) { +// log.info("暂无电话结果回填任务执行"); +// return; +// } +// // 所有随机邀请的规则ID +// Map submitKeys = new HashMap<>(experts.size()); +// Set randomRuleIds = experts.stream().peek(w -> submitKeys.put(w.getId(), w.getSubmitKey())) +// .map(MeetingExpert::getRuleId).collect(Collectors.toSet()); +// // 查询随机邀请回调等待时间 +// Map callbackMinutes = new HashMap<>(randomRuleIds.size()); +// if (!randomRuleIds.isEmpty()) { +// List inviteRules = inviteRuleService.listByIds(randomRuleIds); +// inviteRules.forEach(w -> { +// RandomInviteRuleDTO rule = JSON.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class); +// callbackMinutes.put(w.getId(), rule.getWaitForCallbackMinutes()); +// }); +// } +// LambdaQueryWrapper msgRecordDetailQuery = Wrappers.lambdaQuery(SysMsgRecordDetail.class) +// .in(SysMsgRecordDetail::getSubmitKey, submitKeys.values()); +// List recordDetailList = msgRecordDetailService.list(msgRecordDetailQuery); +// if (recordDetailList.isEmpty()) { +// return; +// } +// Map recordDetailMap = recordDetailList.stream() +// .collect(Collectors.toMap(w -> w.getSubmitKey() + StrPool.UNDERSCORE + w.getReceiveNumber(), w -> w)); +// List 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 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 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 experts) { +// /*List meetingIds = CollUtils.fieldList(experts, MeetingExpert::getMeetingId); +// LambdaQueryWrapper mQuery = Wrappers.lambdaQuery(Meeting.class) +// .eq(Meeting::getStatus, Manager.CANCELED.getCode()) +// .in(Meeting::getId, meetingIds); +// Map meetingMap = CollUtils.listToMap(meetingService.list(mQuery), Meeting::getId); +// if (meetingMap.size() > 0) { +// Map> expertList = new HashMap<>(16); +// experts.forEach(w -> { +// Meeting meeting = meetingMap.get(w.getMeetingId()); +// if (meeting == null) { +// return; +// } +// List 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 contexts = YxtSmsContextBuilder.smsToExpertByCancelMeeting(m, mes, meetingType); +// yxtCallOrSmsHelper.sendSms(contexts); +// }); +// } +// }*/ +// } +// +//} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteExecutorConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteExecutorConfig.java new file mode 100644 index 0000000..8e2d3f5 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteExecutorConfig.java @@ -0,0 +1,29 @@ +package com.ningdatech.pmapi.meeting.task; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +/** + *

+ * ExpertInviteExecutorConfig + *

+ * + * @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; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java new file mode 100644 index 0000000..53b0c6e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertInviteTask.java @@ -0,0 +1,326 @@ +package com.ningdatech.pmapi.meeting.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ningdatech.basic.util.CollUtils; +import com.ningdatech.cache.model.cache.CacheKey; +import com.ningdatech.cache.repository.CachePlusOps; +import com.ningdatech.pmapi.common.util.SpringContextHolder; +import com.ningdatech.pmapi.meeting.builder.ExpertInviteBuilder; +import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +import com.ningdatech.pmapi.meeting.entity.dto.AvoidInfoDTO; +import com.ningdatech.pmapi.meeting.entity.dto.ExpertChooseDTO; +import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType; +import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper; +import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper; +import com.ningdatech.pmapi.meeting.manage.ExpertInviteManage; +import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService; +import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +import com.ningdatech.pmapi.meeting.service.IMeetingService; +import com.ningdatech.pmapi.user.util.LoginUserUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; + + +/** + *

+ * ExpertInviteTask + *

+ * + * @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> INVITE_MAP = new ConcurrentHashMap<>(); + + @PostConstruct + public void initTask() { + if (!properties.getEnable()) { + log.warn("随机邀请已关闭……"); + return; + } + initInviteTaskByStart(); + } + + /** + * 项目重启之后重新初始化邀请任务 + */ + private void initInviteTaskByStart() { + Set 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 expertInviteRules = inviteRuleService.listByMeetingId(meetingId); + Function groupKey = w -> ExpertInviteType.getByCode(w.getInviteType()); + Map> groupByType = CollUtils.group(expertInviteRules, groupKey); + List randomRules = groupByType.get(ExpertInviteType.RANDOM); + if (CollUtil.isEmpty(randomRules)) { + return false; + } + Map ruleMap = CollUtils.listToMap(randomRules, ExpertInviteRule::getId); + LambdaQueryWrapper query = Wrappers.lambdaQuery(MeetingExpert.class) + .in(MeetingExpert::getRuleId, ruleMap.keySet()) + .in(MeetingExpert::getStatus, ExpertAttendStatus.AGREED.getCode()); + List 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 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 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 singletonList = Collections.singletonList(meetingId); + List tempExperts = meetingExpertService.listExpertLastByMeetingIds(singletonList); + Map expertMap = CollUtils.listToMap(tempExperts, MeetingExpert::getExpertId); + Map replacedMap = expertMap.values().stream().filter(w -> w.getPreId() > 0) + .collect(Collectors.toMap(MeetingExpert::getPreId, w -> w)); + Map 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 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 countByAgree(Map expertMap, Map 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); + } + + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/MeetingInviteCompleteNoticeTask.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/MeetingInviteCompleteNoticeTask.java new file mode 100644 index 0000000..67431cb --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/MeetingInviteCompleteNoticeTask.java @@ -0,0 +1,93 @@ +//package com.ningdatech.pmapi.meeting.task; +// +//import com.alibaba.fastjson.JSONObject; +//import com.baomidou.mybatisplus.core.toolkit.StringUtils; +//import com.baomidou.mybatisplus.core.toolkit.Wrappers; +//import com.ningdatech.pmapi.meeting.builder.YxtSmsContextBuilder; +//import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule; +//import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +//import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus; +//import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus; +//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper; +//import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService; +//import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +//import com.ningdatech.pmapi.meeting.service.IMeetingService; +//import lombok.AllArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +//import org.springframework.stereotype.Component; +// +//import javax.annotation.PostConstruct; +//import java.time.Duration; +//import java.time.Instant; +//import java.time.temporal.ChronoUnit; +//import java.util.HashSet; +//import java.util.List; +//import java.util.Objects; +//import java.util.Set; +// +///** +// * @author liuxinxin +// * @date 2023/2/2 上午9:57 +// */ +// +//@Slf4j +//@Component +//@AllArgsConstructor +//public class MeetingInviteCompleteNoticeTask { +// +// private final RandomInviteProperties randomInviteProperties; +// private final IMeetingService meetingService; +// private final IMeetingExpertService meetingExpertService; +// private final ThreadPoolTaskScheduler scheduler; +// private final RedisUtils redisUtils; +// private final static String MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY = "MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY"; +// private final YxtCallOrSmsHelper yxtCallOrSmsHelper; +// private final IExpertInviteRuleService iExpertInviteRuleService; +// +// @PostConstruct +// public void initTask() { +// Instant startTime = Instant.now().plus(randomInviteProperties.getMeetingInviteCompleteNoticeRate(), ChronoUnit.MINUTES); +// // 处理电话结果回填 +// scheduler.scheduleAtFixedRate(this::meetingInviteCompleteNotice, startTime, Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate())); +// } +// +// public void meetingInviteCompleteNotice() { +// log.info("开始执行抽取完成通知:{}", Thread.currentThread().getName()); +// List meetingIdList = meetingService.list(Wrappers.lambdaQuery(Meeting.class) +// .eq(Meeting::getStatus, MeetingStatus.Manager.UNCOMPLETED.getCode()) +// .eq(Meeting::getSendMeetingNotice, false) +// .eq(Meeting::getStopRandomInvite, true)); +// +// Set completeNoticedMeetingIdList = getCompleteNoticedMeetingIdList(); +// for (Meeting meeting : meetingIdList) { +// Long meetingId = meeting.getId(); +// if (completeNoticedMeetingIdList.add(meetingId)) { +// List 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 getCompleteNoticedMeetingIdList() { +// Object result = redisUtils.get(MEETING_INVITE_COMPLETE_NOTICED_MEETING_ID_CACHE_KEY); +// HashSet completeNoticedMeetingIdSet = new HashSet<>(); +// if (Objects.nonNull(result)) { +// String resultStr = result.toString(); +// List completeNoticedMeetingIdList = JSONObject.parseArray(resultStr, Long.class); +// completeNoticedMeetingIdSet = new HashSet<>(completeNoticedMeetingIdList); +// } +// return completeNoticedMeetingIdSet; +// } +// +//} \ No newline at end of file diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/NoticeExpertAt24HoursBeforeMeetingTask.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/NoticeExpertAt24HoursBeforeMeetingTask.java new file mode 100644 index 0000000..4d03873 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/NoticeExpertAt24HoursBeforeMeetingTask.java @@ -0,0 +1,160 @@ +//package com.ningdatech.pmapi.meeting.task; +// +//import cn.hutool.crypto.SecureUtil; +//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +//import com.baomidou.mybatisplus.core.toolkit.Wrappers; +//import com.ningdatech.basic.util.CollUtils; +//import com.ningdatech.basic.util.StrPool; +//import com.ningdatech.cache.model.cache.CacheHashKey; +//import com.ningdatech.cache.repository.CachePlusOps; +//import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService; +//import com.ningdatech.pmapi.meeting.entity.domain.Meeting; +//import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert; +//import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatus; +//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper; +//import com.ningdatech.pmapi.meeting.service.IMeetingExpertService; +//import com.ningdatech.pmapi.meeting.service.IMeetingService; +//import com.ningdatech.pmapi.meta.helper.DictionaryCache; +//import lombok.AllArgsConstructor; +//import lombok.Data; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.scheduling.annotation.Scheduled; +//import org.springframework.stereotype.Component; +// +//import java.time.LocalDateTime; +//import java.time.temporal.ChronoUnit; +//import java.util.ArrayList; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +//import java.util.stream.Collectors; +// +///** +// *

+// * NoticeExpertAt24HoursBeforeMeetingTask +// *

+// * +// * @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 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 getNoticeKey(Long meetingId) { +// Map 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())); +// } +// +// /** +// * 查询需要发送通知的会议: +// *
    1. 未完成
+// *
    2. 创建邀请
+// *
    3. 当前时间距离会议开始时间不足24小时
+// * +// * @return 会议信息 +// * @author WendyYang +// **/ +// private Map loadMeetingAt24HoursBeforeMeeting() { +// LambdaQueryWrapper 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 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 meetingsMap = loadMeetingAt24HoursBeforeMeeting(); +// if (meetingsMap.isEmpty()) { +// return; +// } +// List expertList = meetingExpertService.listExpertByAgreeAttend(meetingsMap.keySet()); +// if (expertList.isEmpty()) { +// return; +// } +// List contexts = new ArrayList<>(); +// Map> expertGroup = CollUtils.group(expertList, MeetingExpert::getMeetingId); +// expertGroup.forEach((meetingId, experts) -> { +// Map noticeCache = new HashMap<>(16); +// Meeting meeting = meetingsMap.get(meetingId); +// String md5 = addressDetailMd5(meeting); +// Map 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); +// } +// } +// +//} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/RandomInviteProperties.java b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/RandomInviteProperties.java new file mode 100644 index 0000000..f69b4b5 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/RandomInviteProperties.java @@ -0,0 +1,40 @@ +package com.ningdatech.pmapi.meeting.task; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + *

+ * TelInviteTaskProperties + *

+ * + * @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; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java index 1767942..975df53 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/bean/vo/ProcessProgressDetailVo.java @@ -24,14 +24,13 @@ public class ProcessProgressDetailVo { * 流程处理详情 */ private ProcessProgressVo processProgressVo; - /** * 流程状态 */ private String status; /** - * 发起人 + * 项目ID */ - private ProcessInstanceUserDto startUser; + private Long projectId; } diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java index cf31e16..a51ecd0 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/controller/TodoCenterController.java @@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import com.ningdatech.pmapi.todocenter.bean.vo.ProcessProgressDetailVo; +import com.ningdatech.pmapi.todocenter.model.dto.req.ProcessDetailReq; import com.ningdatech.pmapi.todocenter.model.dto.req.ToBeProcessedExportReq; import com.wflow.workflow.bean.dto.ReqProcessHandlerDTO; import org.springframework.web.bind.annotation.*; @@ -60,12 +61,12 @@ public class TodoCenterController { /** * 查询流程表单数据及审批的进度步骤 - * @param instanceId 流程实例ID + * @param request 请求参数 * @return 流程进度及表单详情 */ - @GetMapping("/progress/{instanceId}") - public ProcessProgressDetailVo getProcessDetail(@PathVariable String instanceId) { - return todoCenterManage.getProcessDetail(instanceId); + @GetMapping("/progress/detail") + public ProcessProgressDetailVo getProcessDetail(@Valid @ModelAttribute ProcessDetailReq request) { + return todoCenterManage.getProcessDetail(request); } /** diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/SaveCommentCmd.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/SaveCommentCmd.java index b5f18da..8167e60 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/SaveCommentCmd.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/extension/cmd/SaveCommentCmd.java @@ -1,5 +1,6 @@ package com.ningdatech.pmapi.todocenter.extension.cmd; +import com.ningdatech.pmapi.common.constant.CommonConstant; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableObjectNotFoundException; import org.flowable.common.engine.impl.identity.Authentication; @@ -85,8 +86,8 @@ public class SaveCommentCmd implements Command { comment.setProcessInstanceId(this.processInstanceId); comment.setAction("AddComment"); String eventMessage = this.message.replaceAll("\\s+", " "); - if (eventMessage.length() > 163) { - eventMessage = eventMessage.substring(0, 160) + "..."; + if (eventMessage.length() > CommonConstant.COMMENT_MAX_SIZE) { + eventMessage = eventMessage.substring(CommonConstant.SUB_COMMENT_SIZE_MIN, CommonConstant.SUB_COMMENT_SIZE_MAX) + "..."; } comment.setMessage(eventMessage); diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java index 6ecff05..1b8a934 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java @@ -5,14 +5,12 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.common.collect.Sets; import com.ningdatech.basic.exception.BizException; -import com.ningdatech.basic.model.GenericResult; import com.ningdatech.basic.model.PageVo; import com.ningdatech.pmapi.common.constant.CommonConstant; import com.ningdatech.pmapi.common.model.entity.ExcelExportWriter; import com.ningdatech.pmapi.common.statemachine.event.ProjectStatusChangeEvent; import com.ningdatech.pmapi.common.statemachine.util.StateMachineUtils; import com.ningdatech.pmapi.common.util.ExcelDownUtil; -import com.ningdatech.pmapi.common.util.SendWorkNoticeUtil; import com.ningdatech.pmapi.organization.model.entity.DingEmployeeInfo; import com.ningdatech.pmapi.organization.model.entity.DingOrganization; import com.ningdatech.pmapi.organization.service.IDingEmployeeInfoService; @@ -26,6 +24,7 @@ import com.ningdatech.pmapi.projectlib.service.IProjectService; import com.ningdatech.pmapi.todocenter.bean.entity.WorkNoticeInfo; import com.ningdatech.pmapi.todocenter.bean.vo.ProcessProgressDetailVo; import com.ningdatech.pmapi.todocenter.enumeration.IsAppendProjectEnum; +import com.ningdatech.pmapi.todocenter.model.dto.req.ProcessDetailReq; import com.ningdatech.pmapi.todocenter.model.dto.req.ToBeProcessedReq; import com.ningdatech.pmapi.todocenter.model.dto.req.ToBeProcessedExportReq; import com.ningdatech.pmapi.todocenter.model.dto.vo.ResToBeProcessedVO; @@ -56,10 +55,6 @@ import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; import java.time.LocalDateTime; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.stream.Collectors; import static cn.hutool.core.collection.CollUtil.isEmpty; @@ -105,7 +100,7 @@ public class TodoCenterManage { return PageVo.empty(); } List instCodes = userTodoList.stream().map(ProcessTaskVo::getInstanceId).collect(Collectors.toList()); - + Map taskVoMap = userTodoList.stream().collect(Collectors.toMap(ProcessTaskVo::getInstanceId, v -> v)); //2.再分页查询项目信息 ProjectListReq projectListReq = new ProjectListReq(); projectListReq.setInstCodes(instCodes); @@ -120,6 +115,8 @@ public class TodoCenterManage { ResToBeProcessedVO res = new ResToBeProcessedVO(); BeanUtils.copyProperties(d, res); res.setProjectId(d.getId()); + ProcessTaskVo taskVo = taskVoMap.get(d.getInstCode()); + res.setNodeId(taskVo.getTaskDefKey()); res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); res.setProcessLaunchTime(d.getCreateOn()); return res; @@ -555,11 +552,21 @@ public class TodoCenterManage { /** * 查询流程表单数据及审批的进度步骤 - * @param instanceId 流程实例ID + * + * @param request 请求参数 * @return 流程进度及表单详情 */ - public ProcessProgressDetailVo getProcessDetail(String instanceId) { - ProcessProgressVo progressInstanceDetail = processInstanceService.getProgressInstanceDetail(null, instanceId); + public ProcessProgressDetailVo getProcessDetail(ProcessDetailReq request) { + String instanceId = request.getInstanceId(); + String nodeId = request.getNodeId(); + Long projectId = request.getProjectId(); + ProcessProgressVo progressInstanceDetail = null; + if (Objects.isNull(nodeId)) { + progressInstanceDetail = processInstanceService.getProgressInstanceDetail(null, instanceId); + }else { + progressInstanceDetail = processInstanceService.getProgressInstanceDetail(nodeId, instanceId); + } + List progressInfo = progressInstanceDetail.getProgressInfo(); Set userSet = Sets.newHashSet(); @@ -580,6 +587,7 @@ public class TodoCenterManage { ProcessProgressDetailVo res = new ProcessProgressDetailVo(); res.setProcessProgressVo(progressInstanceDetail); res.setStatus(progressInstanceDetail.getStatus()); + res.setProjectId(projectId); return res; } @@ -796,6 +804,8 @@ public class TodoCenterManage { return PageVo.empty(); } List instCodes = ccMeList.stream().map(ProcessInstanceVo::getInstanceId).collect(Collectors.toList()); + Map instanceVoMap = ccMeList.stream() + .collect(Collectors.toMap(ProcessInstanceVo::getInstanceId, v -> v)); // 分页查询项目信息 ProjectListReq projectListReq = new ProjectListReq(); @@ -810,6 +820,8 @@ public class TodoCenterManage { List resVos = projectPage.getRecords().stream().map(d -> { ResToBeProcessedVO res = new ResToBeProcessedVO(); BeanUtils.copyProperties(d, res); + ProcessInstanceVo instanceVo = instanceVoMap.get(d.getInstCode()); + res.setNodeId(instanceVo.getNodeId()); res.setProjectId(d.getId()); res.setProcessStatusName(ProcessStatusEnum.getDescByCode(d.getProcessStatus())); res.setProcessLaunchTime(d.getCreateOn()); diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ProcessDetailReq.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ProcessDetailReq.java new file mode 100644 index 0000000..df66044 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/req/ProcessDetailReq.java @@ -0,0 +1,30 @@ +package com.ningdatech.pmapi.todocenter.model.dto.req; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * 流程处理进度详情请求参数 + * + * @author CMM + * @since 2023/02/24 10:15 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ProcessDetailReq { + + @ApiModelProperty("流程实例ID") + @NotNull + private String instanceId; + + @ApiModelProperty("流程节点ID") + private String nodeId; + + @ApiModelProperty("项目ID") + private Long projectId; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/vo/ResToBeProcessedVO.java b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/vo/ResToBeProcessedVO.java index 0b02d4b..fee797c 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/vo/ResToBeProcessedVO.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/todocenter/model/dto/vo/ResToBeProcessedVO.java @@ -32,6 +32,9 @@ public class ResToBeProcessedVO implements Serializable { @ApiModelProperty("流程实例编号") private String instCode; + @ApiModelProperty("待处理节点ID") + private String nodeId; + @ApiModelProperty("项目名称") private String projectName; diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/util/LoginUserUtil.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/util/LoginUserUtil.java index f686cb7..ef62af4 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/util/LoginUserUtil.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/util/LoginUserUtil.java @@ -2,6 +2,7 @@ package com.ningdatech.pmapi.user.util; import com.ningdatech.basic.auth.AbstractLoginUserUtil; import com.ningdatech.pmapi.sys.model.entity.Role; +import com.ningdatech.pmapi.user.entity.enumeration.RoleEnum; import com.ningdatech.pmapi.user.security.auth.model.UserInfoDetails; import java.util.List; @@ -25,4 +26,9 @@ public class LoginUserUtil extends AbstractLoginUserUtil { return userRoleList.stream().map(Role::getId).collect(Collectors.toList()); } + public static boolean isExpert() { + UserInfoDetails detail = loginUserDetail(); + return detail.getUserRoleList().stream().anyMatch(w -> RoleEnum.EXPERT.eq(w.getCode())); + } + }