Browse Source

Merge remote-tracking branch 'origin/master'

tags/24080901
niohe·erbao 1 year ago
parent
commit
06d29d14f1
14 changed files with 360 additions and 313 deletions
  1. +1
    -3
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java
  2. +3
    -3
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java
  3. +2
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java
  4. +18
    -9
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java
  5. +20
    -9
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java
  6. +1
    -1
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java
  7. +252
    -241
      pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java
  8. +7
    -4
      pmapi/src/main/java/com/ningdatech/pmapi/organization/service/impl/DingOrganizationServiceImpl.java
  9. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/sms/constant/VoiceSmsTemplateConst.java
  10. +0
    -13
      pmapi/src/main/java/com/ningdatech/pmapi/sms/constant/YxtSmsTemplateConst.java
  11. +2
    -2
      pmapi/src/main/java/com/ningdatech/pmapi/sms/manage/SmsManage.java
  12. +15
    -15
      pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java
  13. +7
    -6
      pmapi/src/main/java/com/ningdatech/pmapi/user/service/impl/UserInfoServiceImpl.java
  14. +4
    -5
      pmapi/src/test/java/com/ningdatech/pmapi/todocenter/TodoCenterTest.java

+ 1
- 3
pmapi/src/main/java/com/ningdatech/pmapi/meeting/controller/MeetingController.java View File

@@ -8,11 +8,9 @@ import com.ningdatech.pmapi.meeting.entity.dto.ReviewProjectDTO;
import com.ningdatech.pmapi.meeting.entity.req.*;
import com.ningdatech.pmapi.meeting.entity.vo.*;
import com.ningdatech.pmapi.meeting.manage.MeetingManage;
import com.ningdatech.pmapi.projectlib.model.vo.ProjectLibListItemVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@@ -165,7 +163,7 @@ public class MeetingController {
@ApiOperation("重发短信")
@PostMapping("/confirmedRoster")
@WebLog(value = "重发短信")
public void resendSms(MeetingCancelReq req) {
public void resendSms(@RequestBody MeetingCancelReq req) {
meetingManage.confirmedRoster(req.getMeetingId());
}



+ 3
- 3
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/dto/MeetingBasicDTO.java View File

@@ -36,12 +36,12 @@ public class MeetingBasicDTO {

@NotNull(message = "开始时间不能为空")
@ApiModelProperty("开始时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime startTime;

@NotNull(message = "结束时间不能为空")
@ApiModelProperty("结束时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime endTime;

@NotNull(message = "评审耗时不能为空")
@@ -50,7 +50,7 @@ public class MeetingBasicDTO {

@NotNull(message = "评委出席时间不能为空")
@ApiModelProperty("评委出席时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime judgesAttendanceTime;

@NotEmpty(message = "评审地点不能为空")


+ 2
- 2
pmapi/src/main/java/com/ningdatech/pmapi/meeting/entity/req/MeetingListReq.java View File

@@ -26,11 +26,11 @@ public class MeetingListReq extends PagePo {
private String name;

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

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

@ApiModelProperty("事务状态")


+ 18
- 9
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/MeetingManageHelper.java View File

@@ -23,6 +23,7 @@ import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingBasicDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteTypeEnum;
import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq;
import com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVO;
import com.ningdatech.pmapi.meeting.entity.vo.MeetingByManagerVO;
@@ -194,20 +195,28 @@ public class MeetingManageHelper {
/**
* 校验是否能够进行指定邀请
*
* @param meetingId 会议ID
* @param expertIds 专家ID
* @param meetingId 会议ID
* @param expertIds 专家ID
* @param inviteType 邀请类型
* @return 符合邀请规则的专家
* @author WendyYang
**/
public List<ExpertUserFullInfo> appointExpertCheck(Long meetingId, List<Long> expertIds) {
public List<ExpertUserFullInfo> appointExpertCheck(Long meetingId, Integer inviteType, List<Long> expertIds) {
List<ExpertUserFullInfo> experts = expertUserFullInfoService.listByUserId(expertIds);
AvoidRuleDTO avoidRule = getAvoidInfoDto(meetingId);
AvoidRuleDTO avoidRule;
if (ExpertInviteTypeEnum.RANDOM.eq(inviteType)) {
avoidRule = getAvoidInfoDto(meetingId);
} else {
avoidRule = null;
}
experts.forEach(expert -> {
if (avoidRule.getAvoidUnitIdList().contains(expert.getCompany())) {
throw BizException.wrap("请移除已回避单位的专家");
}
if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds()) && avoidRule.getExpertIds().contains(expert.getUserId())) {
throw BizException.wrap("请移除已回避的专家");
if (avoidRule != null) {
if (avoidRule.getAvoidUnitIdList().contains(expert.getCompany())) {
throw BizException.wrap("请移除已回避单位的专家");
}
if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds()) && avoidRule.getExpertIds().contains(expert.getUserId())) {
throw BizException.wrap("请移除已回避的专家");
}
}
// 校验专家状态
ExpertAccountStatusEnum accountStatus = ExpertAccountStatusEnum.of(expert.getExpertAccountStatus());


+ 20
- 9
pmapi/src/main/java/com/ningdatech/pmapi/meeting/helper/YxtCallOrSmsHelper.java View File

@@ -1,13 +1,24 @@
package com.ningdatech.pmapi.meeting.helper;

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.sms.constant.VoiceSmsTemplateConst;
import com.ningdatech.pmapi.sms.utils.DateUtil;
import com.ningdatech.yxt.client.YxtClient;
import com.ningdatech.yxt.constants.YxtSmsSignEnum;
import com.ningdatech.yxt.model.cmd.SendSmsCmd;
import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext;
import com.ningdatech.yxt.model.cmd.SubmitTaskCallResponse;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

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

import static com.ningdatech.yxt.model.cmd.SubmitTaskCallCmd.SubmitTaskCallContext;
import static com.ningdatech.yxt.model.cmd.SubmitTaskCallCmd.of;

/**
* <p>
* YxtCallOrSmsHelper
@@ -20,24 +31,24 @@ import java.util.List;
@AllArgsConstructor
public class YxtCallOrSmsHelper {

// private final YinXinTongClient yinXinTongClient;
private final YxtClient yxtClient;

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

/*public void sendSms(List<SendSmsContext> smsList) {
yinXinTongClient.submitSmsTask(new SendSmsCmd() {{
public void sendSms(List<SendSmsContext> smsList) {
yxtClient.submitSmsTask(new SendSmsCmd() {{
setContextList(smsList);
setSmsSignEnum(YxtSmsSignEnum.ZJS_ELECTRONIC_EXPERT_LIB);
}});
@@ -45,6 +56,6 @@ public class YxtCallOrSmsHelper {

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

}

+ 1
- 1
pmapi/src/main/java/com/ningdatech/pmapi/meeting/manage/MeetingManage.java View File

@@ -559,7 +559,7 @@ public class MeetingManage {
throw BizException.wrap("补充专家失败");
}
AppointInviteRuleDTO rule = inviteRuleService.appointRuleByMeetingId(meetingId);
List<ExpertUserFullInfo> userInfos = meetingManageHelper.appointExpertCheck(meetingId, req.getExpertIdList());
List<ExpertUserFullInfo> userInfos = meetingManageHelper.appointExpertCheck(meetingId, meeting.getInviteType(), req.getExpertIdList());
List<MeetingExpert> expertList = CollUtils.convert(userInfos, w -> {
MeetingExpert me = ExpertInviteBuilder.getExpertByAppoint(meetingId, w, rule.getId());
me.setStatus(ExpertAttendStatusEnum.NOTICING.getCode());


+ 252
- 241
pmapi/src/main/java/com/ningdatech/pmapi/meeting/task/ExpertCallResultRewriteTask.java View File

@@ -1,241 +1,252 @@
//package com.ningdatech.pmapi.meeting.task;
//
//import com.alibaba.fastjson.JSON;
//import com.alibaba.fastjson.JSONObject;
//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
//import com.ningdatech.basic.util.StrPool;
//import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
//import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
//import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
//import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
//import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
//import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
//import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteType;
//import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
//import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
//import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
//import com.ningdatech.pmapi.meeting.service.IMeetingService;
//import com.ningdatech.pmapi.meta.helper.DictionaryCache;
//import com.ningdatech.pmapi.sms.utils.DateUtil;
//import lombok.AllArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
//import org.springframework.stereotype.Component;
//
//import javax.annotation.PostConstruct;
//import java.time.Duration;
//import java.time.Instant;
//import java.time.LocalDateTime;
//import java.time.temporal.ChronoUnit;
//import java.util.*;
//import java.util.stream.Collectors;
//
//import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus.AGREED;
//import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatus.NOTICING;
//
//
///**
// * <p>
// * ExpertCallResultRewriteTask
// * <br>
// * 专家电话结果回填
// * </p>
// *
// * @author WendyYang
// * @since 14:15 2022/8/18
// */
//@Slf4j
//@Component
//@AllArgsConstructor
//public class ExpertCallResultRewriteTask {
//
// private final RandomInviteProperties randomInviteProperties;
//
// private final ThreadPoolTaskScheduler scheduler;
// private final IMeetingExpertService meetingExpertService;
// private final IExpertInviteRuleService inviteRuleService;
// private final IMeetingService meetingService;
// private final ISysMsgRecordDetailService msgRecordDetailService;
// private final DictionaryCache dictionaryCache;
// private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
// private final IExpertUserFullInfoService iExpertUserFullInfoService;
//
// @PostConstruct
// public void initTask() {
// if (!randomInviteProperties.getEnable()) {
// log.warn("随机邀请已关闭……");
// return;
// }
// Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES);
// // 处理电话结果回填
// scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()));
// }
//
//
// public void rewritePhoneCallResult() {
// log.info("开始执行电话结果回填任务:{}", Thread.currentThread().getName());
// // 查询所有邀请的专家信息 状态为通话中的
// LambdaQueryWrapper<MeetingExpert> meQuery = Wrappers.lambdaQuery(MeetingExpert.class)
// .eq(MeetingExpert::getStatus, NOTICING.getCode())
// .eq(MeetingExpert::getInviteType, ExpertInviteType.RANDOM.getCode());
// List<MeetingExpert> experts = meetingExpertService.list(meQuery);
// if (experts.isEmpty()) {
// log.info("暂无电话结果回填任务执行");
// return;
// }
// // 所有随机邀请的规则ID
// Map<Long, String> submitKeys = new HashMap<>(experts.size());
// Set<Long> randomRuleIds = experts.stream().peek(w -> submitKeys.put(w.getId(), w.getSubmitKey()))
// .map(MeetingExpert::getRuleId).collect(Collectors.toSet());
// // 查询随机邀请回调等待时间
// Map<Long, Integer> callbackMinutes = new HashMap<>(randomRuleIds.size());
// if (!randomRuleIds.isEmpty()) {
// List<ExpertInviteRule> inviteRules = inviteRuleService.listByIds(randomRuleIds);
// inviteRules.forEach(w -> {
// RandomInviteRuleDTO rule = JSON.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class);
// callbackMinutes.put(w.getId(), rule.getWaitForCallbackMinutes());
// });
// }
// LambdaQueryWrapper<SysMsgRecordDetail> msgRecordDetailQuery = Wrappers.lambdaQuery(SysMsgRecordDetail.class)
// .in(SysMsgRecordDetail::getSubmitKey, submitKeys.values());
// List<SysMsgRecordDetail> recordDetailList = msgRecordDetailService.list(msgRecordDetailQuery);
// if (recordDetailList.isEmpty()) {
// return;
// }
// Map<String, SysMsgRecordDetail> recordDetailMap = recordDetailList.stream()
// .collect(Collectors.toMap(w -> w.getSubmitKey() + StrPool.UNDERSCORE + w.getReceiveNumber(), w -> w));
// List<MeetingExpert> updates = new ArrayList<>(), agrees = new ArrayList<>();
// for (MeetingExpert expert : experts) {
// String key = expert.getSubmitKey() + StrPool.UNDERSCORE + expert.getMobile();
// SysMsgRecordDetail msgRecordDetail = recordDetailMap.get(key);
// if (msgRecordDetail == null) {
// // 极端情况下获取不到submitKey异常情况
// continue;
// }
// Integer minutes = callbackMinutes.get(expert.getRuleId());
// Optional<Integer> status = getStatusByMsgRecordDetail(msgRecordDetail, minutes, expert.getCreateOn());
// if (status.isPresent()) {
// MeetingExpert update = new MeetingExpert();
// update.setUpdateBy(0L);
// update.setUpdateOn(LocalDateTime.now());
// update.setId(expert.getId());
// update.setPreStatus(expert.getStatus());
// update.setStatus(status.get());
// if (AGREED.eq(update.getStatus())) {
// sendAgreeNotice(expert);
// agrees.add(expert);
// }
// updates.add(update);
// }
// }
// meetingExpertService.updateBatchById(updates);
// if (agrees.size() > 0) {
// obtainCallBackAfterMeetingCanceled(agrees);
// }
// }
//
// private void sendAgreeNotice(MeetingExpert expert) {
// try {
// Meeting meeting = meetingService.getById(expert.getMeetingId());
// if (Objects.isNull(meeting)) {
// return;
// }
// ExpertUserFullInfo expertUserFullInfo = iExpertUserFullInfoService.getByUserId(expert.getExpertId());
// String expertName = null;
// if (Objects.nonNull(expertUserFullInfo)) {
// expertName = expertUserFullInfo.getExpertName();
// }
// String smsContent = String.format(YxtSmsTemplateConst.EXPERT_AGREE_NOTICE, meeting.getHoldCompany(), expertName, meeting.getName()
// , meeting.getStartTime().format(DateUtil.DTF_YMD_HM), meeting.getRegionDetail(), meeting.getConnecter(), meeting.getContact(), WebProperties.webUrl);
// SendSmsContext context = new SendSmsContext();
// context.setContent(smsContent);
// context.setReceiveNumber(expert.getMobile());
//
// yxtCallOrSmsHelper.sendSms(context);
// } catch (Exception e) {
// log.info("发送专家会议接受通知短信失败:{}", JSONObject.toJSONString(expert));
// }
// }
//
// private static Optional<Integer> getStatusByMsgRecordDetail(SysMsgRecordDetail msgRecordDetail, int minutes, LocalDateTime createOn) {
// LocalDateTime time = LocalDateTime.now().minusMinutes(minutes);
// String callBackJson = msgRecordDetail.getCallBackJson();
// if (StrUtils.isBlank(callBackJson) && time.isBefore(createOn)) {
// return Optional.empty();
// }
// ExpertAttendStatus status;
// if (StrUtils.isNotBlank(callBackJson)) {
// try {
// JSONObject callbackObject = JSON.parseObject(callBackJson);
// Date dialBeginTime = callbackObject.getDate("dialBeginTime");
// if (dialBeginTime == null) {
// return Optional.empty();
// }
// Integer resultCode = callbackObject.getInteger("resultCode");
// if (resultCode != null && resultCode == 0) {
// String pressKeyStr = callbackObject.getString("pressKey");
// if (Objects.nonNull(pressKeyStr)) {
// pressKeyStr = pressKeyStr.replaceAll("\\*", "").trim();
// }
// Integer pressKey = null;
// if (StringUtils.isNotBlank(pressKeyStr)) {
// pressKey = Integer.parseInt(pressKeyStr);
// }
// if (pressKey == null) {
// if (time.isBefore(createOn)) {
// return Optional.empty();
// }
// status = REFUSED;
// } else {
// if (pressKey == 1) {
// status = AGREED;
// } else {
// status = REFUSED;
// }
// }
// } else {
// status = NOT_ANSWERED;
// }
// } catch (Exception e) {
// log.error("获取电话回调结果异常", e);
// status = NOT_ANSWERED;
// }
// } else {
// // 超时未回复设置为拒绝参加
// status = REFUSED;
// }
// return Optional.of(status.getCode());
// }
//
// /**
// * 会议取消之后拿到回调结果的话需要发送取消短信
// **/
// private void obtainCallBackAfterMeetingCanceled(List<MeetingExpert> experts) {
// /*List<Long> meetingIds = CollUtils.fieldList(experts, MeetingExpert::getMeetingId);
// LambdaQueryWrapper<Meeting> mQuery = Wrappers.lambdaQuery(Meeting.class)
// .eq(Meeting::getStatus, Manager.CANCELED.getCode())
// .in(Meeting::getId, meetingIds);
// Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetingService.list(mQuery), Meeting::getId);
// if (meetingMap.size() > 0) {
// Map<Meeting, List<MeetingExpert>> expertList = new HashMap<>(16);
// experts.forEach(w -> {
// Meeting meeting = meetingMap.get(w.getMeetingId());
// if (meeting == null) {
// return;
// }
// List<MeetingExpert> list = expertList.computeIfAbsent(meeting, k -> new ArrayList<>());
// list.add(w);
// });
// if (!expertList.isEmpty()) {
// expertList.forEach((m, mes) -> {
// String meetingType = dictionaryCache.getByCode(m.getType()).getName();
// List<SendSmsContext> contexts = YxtSmsContextBuilder.smsToExpertByCancelMeeting(m, mes, meetingType);
// yxtCallOrSmsHelper.sendSms(contexts);
// });
// }
// }*/
// }
//
//}
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.common.util.StrUtils;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.entity.config.WebProperties;
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.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteTypeEnum;
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.constant.VoiceSmsTemplateConst;
import com.ningdatech.pmapi.sms.utils.DateUtil;
import com.ningdatech.yxt.entity.SysMsgRecordDetail;
import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext;
import com.ningdatech.yxt.service.ISysMsgRecordDetailService;
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.ExpertAttendStatusEnum.*;

/**
* <p>
* ExpertCallResultRewriteTask
* <br>
* 专家电话结果回填
* </p>
*
* @author WendyYang
* @since 14:15 2022/8/18
*/
@Slf4j
@Component
@AllArgsConstructor
public class ExpertCallResultRewriteTask {

private final RandomInviteProperties randomInviteProperties;

private final ThreadPoolTaskScheduler scheduler;
private final IMeetingExpertService meetingExpertService;
private final IExpertInviteRuleService inviteRuleService;
private final IMeetingService meetingService;
private final ISysMsgRecordDetailService msgRecordDetailService;
private final DictionaryCache dictionaryCache;
private final YxtCallOrSmsHelper yxtCallOrSmsHelper;
private final IExpertUserFullInfoService userFullInfoService;

@PostConstruct
public void initTask() {
if (!randomInviteProperties.getEnable()) {
log.warn("随机邀请已关闭……");
return;
}
Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES);
// 处理电话结果回填
scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()));
}


public void rewritePhoneCallResult() {
log.info("开始执行电话结果回填任务:{}", Thread.currentThread().getName());
// 查询所有邀请的专家信息 状态为通话中的
LambdaQueryWrapper<MeetingExpert> meQuery = Wrappers.lambdaQuery(MeetingExpert.class)
.eq(MeetingExpert::getStatus, NOTICING.getCode())
.eq(MeetingExpert::getInviteType, ExpertInviteTypeEnum.RANDOM.getCode());
List<MeetingExpert> experts = meetingExpertService.list(meQuery);
if (experts.isEmpty()) {
log.info("暂无电话结果回填任务执行");
return;
}
// 所有随机邀请的规则ID
Map<Long, String> submitKeys = new HashMap<>(experts.size());
Set<Long> randomRuleIds = experts.stream().peek(w -> submitKeys.put(w.getId(), w.getSubmitKey()))
.map(MeetingExpert::getRuleId).collect(Collectors.toSet());
// 查询随机邀请回调等待时间
Map<Long, Integer> callbackMinutes = new HashMap<>(randomRuleIds.size());
if (!randomRuleIds.isEmpty()) {
List<ExpertInviteRule> inviteRules = inviteRuleService.listByIds(randomRuleIds);
inviteRules.forEach(w -> {
RandomInviteRuleDTO rule = JSON.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class);
callbackMinutes.put(w.getId(), rule.getWaitForCallbackMinutes());
});
}
LambdaQueryWrapper<SysMsgRecordDetail> msgRecordDetailQuery = Wrappers.lambdaQuery(SysMsgRecordDetail.class)
.in(SysMsgRecordDetail::getSubmitKey, submitKeys.values());
List<SysMsgRecordDetail> recordDetailList = msgRecordDetailService.list(msgRecordDetailQuery);
if (recordDetailList.isEmpty()) {
return;
}
Map<String, SysMsgRecordDetail> recordDetailMap = recordDetailList.stream()
.collect(Collectors.toMap(w -> w.getSubmitKey() + StrPool.UNDERSCORE + w.getReceiveNumber(), w -> w));
List<MeetingExpert> updates = new ArrayList<>(), agrees = new ArrayList<>();
for (MeetingExpert expert : experts) {
String key = expert.getSubmitKey() + StrPool.UNDERSCORE + expert.getMobile();
SysMsgRecordDetail msgRecordDetail = recordDetailMap.get(key);
if (msgRecordDetail == null) {
// 极端情况下获取不到submitKey异常情况
continue;
}
Integer minutes = callbackMinutes.get(expert.getRuleId());
Optional<Integer> status = getStatusByMsgRecordDetail(msgRecordDetail, minutes, expert.getCreateOn());
if (status.isPresent()) {
MeetingExpert update = new MeetingExpert();
update.setUpdateBy(0L);
update.setUpdateOn(LocalDateTime.now());
update.setId(expert.getId());
update.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 = userFullInfoService.getByUserId(expert.getExpertId());
String expertName = null;
if (Objects.nonNull(expertUserFullInfo)) {
expertName = expertUserFullInfo.getExpertName();
}
String smsContent = String.format(VoiceSmsTemplateConst.EXPERT_AGREE_ATTEND_TEMPLATE,
meeting.getHoldOrg(),
expertName,
meeting.getName(),
meeting.getStartTime().format(DateUtil.DTF_YMD_HM),
meeting.getMeetingAddress(),
meeting.getConnecter(),
meeting.getContact(),
WebProperties.webUrl);
SendSmsContext context = new SendSmsContext();
context.setContent(smsContent);
context.setReceiveNumber(expert.getMobile());
yxtCallOrSmsHelper.sendSms(context);
} catch (Exception e) {
log.info("发送专家会议接受通知短信失败:{}", JSONObject.toJSONString(expert));
}
}

private static Optional<Integer> getStatusByMsgRecordDetail(SysMsgRecordDetail msgRecordDetail, int minutes, LocalDateTime createOn) {
LocalDateTime time = LocalDateTime.now().minusMinutes(minutes);
String callBackJson = msgRecordDetail.getCallBackJson();
if (StrUtils.isBlank(callBackJson) && time.isBefore(createOn)) {
return Optional.empty();
}
ExpertAttendStatusEnum 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 = UNANSWERED;
}
} catch (Exception e) {
log.error("获取电话回调结果异常", e);
status = UNANSWERED;
}
} else {
// 超时未回复设置为拒绝参加
status = REFUSED;
}
return Optional.of(status.getCode());
}

/**
* 会议取消之后拿到回调结果的话需要发送取消短信
**/
private void obtainCallBackAfterMeetingCanceled(List<MeetingExpert> experts) {
/*List<Long> meetingIds = CollUtils.fieldList(experts, MeetingExpert::getMeetingId);
LambdaQueryWrapper<Meeting> mQuery = Wrappers.lambdaQuery(Meeting.class)
.eq(Meeting::getStatus, Manager.CANCELED.getCode())
.in(Meeting::getId, meetingIds);
Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetingService.list(mQuery), Meeting::getId);
if (meetingMap.size() > 0) {
Map<Meeting, List<MeetingExpert>> expertList = new HashMap<>(16);
experts.forEach(w -> {
Meeting meeting = meetingMap.get(w.getMeetingId());
if (meeting == null) {
return;
}
List<MeetingExpert> list = expertList.computeIfAbsent(meeting, k -> new ArrayList<>());
list.add(w);
});
if (!expertList.isEmpty()) {
expertList.forEach((m, mes) -> {
String meetingType = dictionaryCache.getByCode(m.getType()).getName();
List<SendSmsContext> contexts = YxtSmsContextBuilder.smsToExpertByCancelMeeting(m, mes, meetingType);
yxtCallOrSmsHelper.sendSms(contexts);
});
}
}*/
}

}

+ 7
- 4
pmapi/src/main/java/com/ningdatech/pmapi/organization/service/impl/DingOrganizationServiceImpl.java View File

@@ -11,7 +11,10 @@ import com.ningdatech.pmapi.organization.model.entity.DingOrganization;
import com.ningdatech.pmapi.organization.service.IDingOrganizationService;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
@@ -58,7 +61,7 @@ public class DingOrganizationServiceImpl extends ServiceImpl<DingOrganizationMap
public List<String> listNameByCodes(Collection<String> codes) {
LambdaQueryWrapper<DingOrganization> query = Wrappers.lambdaQuery(DingOrganization.class)
.select(DingOrganization::getOrganizationName)
.eq(DingOrganization::getOrganizationCode, codes);
.in(DingOrganization::getOrganizationCode, codes);
return CollUtils.fieldList(list(query), DingOrganization::getOrganizationName);
}

@@ -76,7 +79,7 @@ public class DingOrganizationServiceImpl extends ServiceImpl<DingOrganizationMap
orgCodeList = orgCodeList.stream().distinct().collect(Collectors.toList());
List<DingOrganization> dingOrganizationList = listByCodes(orgCodeList);
return dingOrganizationList.stream()
.filter(r -> StringUtils.isNotBlank(r.getOrganizationCode()) && Objects.nonNull(r))
.collect(Collectors.toMap(DingOrganization::getOrganizationCode, r->r));
.filter(r -> StringUtils.isNotBlank(r.getOrganizationCode()))
.collect(Collectors.toMap(DingOrganization::getOrganizationCode, r -> r));
}
}

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/sms/constant/VoiceSmsTemplateConst.java View File

@@ -0,0 +1,28 @@
package com.ningdatech.pmapi.sms.constant;

/**
* <p>
* VoiceSmsTemplateConst
* </p>
*
* @author WendyYang
* @since 15:10 2023/3/30
*/
public interface VoiceSmsTemplateConst {

/**
* 短信登陆验证码
*/
String SMS_LOGIN_TEMPLATE = "验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。";

/**
* 专家电话通知语音模版
*/
String OFFLINE_TEMPLATE = "尊敬的专家您好,%s现邀请您作为专家参加%s会议,会议时间:%s,会议地点:%s。 确认参加请按 1,拒绝参加请按 2。请您选择";

/**
* 专家同意参加短信通知模板
*/
String EXPERT_AGREE_ATTEND_TEMPLATE = "";

}

+ 0
- 13
pmapi/src/main/java/com/ningdatech/pmapi/sms/constant/YxtSmsTemplateConst.java View File

@@ -1,13 +0,0 @@
package com.ningdatech.pmapi.sms.constant;

/**
* @author liuxinxin
* @date 2022/8/8 下午5:05
*/
public interface YxtSmsTemplateConst {

/**
* 短信登陆验证码
*/
String SMS_LOGIN_TEMPLATE = "验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。";
}

+ 2
- 2
pmapi/src/main/java/com/ningdatech/pmapi/sms/manage/SmsManage.java View File

@@ -6,7 +6,7 @@ import com.ningdatech.basic.exception.BizException;
import com.ningdatech.cache.model.cache.CacheKey;
import com.ningdatech.cache.repository.CachePlusOps;
import com.ningdatech.pmapi.sms.constant.VerificationCodeType;
import com.ningdatech.pmapi.sms.constant.YxtSmsTemplateConst;
import com.ningdatech.pmapi.sms.constant.VoiceSmsTemplateConst;
import com.ningdatech.pmapi.sms.model.dto.VerifyCodeCacheDTO;
import com.ningdatech.pmapi.sms.model.po.ReqVerificationCodePO;
import com.ningdatech.pmapi.sms.utils.DateUtil;
@@ -69,7 +69,7 @@ public class SmsManage {
case LOGIN:
SendSmsCmd.SendSmsContext sendSmsContext = new SendSmsCmd.SendSmsContext();
sendSmsContext.setReceiveNumber(request.getMobile());
sendSmsContext.setContent(String.format(YxtSmsTemplateConst.SMS_LOGIN_TEMPLATE, code, verificationCodeTypeEnum.getExpireTime()));
sendSmsContext.setContent(String.format(VoiceSmsTemplateConst.SMS_LOGIN_TEMPLATE, code, verificationCodeTypeEnum.getExpireTime()));
sendSmsCmd.setContextList(Collections.singletonList(sendSmsContext));
sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.ZJS_ELECTRONIC_EXPERT_LIB);
break;


+ 15
- 15
pmapi/src/main/java/com/ningdatech/pmapi/todocenter/manage/TodoCenterManage.java View File

@@ -316,10 +316,6 @@ public class TodoCenterManage {
HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
// 获取流程发起人信息
String startUserId = instance.getStartUserId();
UserInfo startUserInfo = userInfoService.getOne(Wrappers.lambdaQuery(UserInfo.class)
.eq(UserInfo::getEmployeeCode, startUserId));
// 获取流程定义名称
String processDefinitionName = instance.getProcessDefinitionName();

@@ -339,15 +335,15 @@ public class TodoCenterManage {
// 获取流程通过后当前审核人信息,向其发送工作通知
List<ProgressNode> newProgressInfo = newInstanceDetail.getProgressInfo();
ProgressNode currentNode = newProgressInfo.get(newProgressInfo.size() - 1);
UserInfo auditUserInfo;
String currentEmployeeCode;
// 说明当前节点是子流程节点
if (currentNode.getNodeType().name().equals(NodeTypeEnum.SUB.name())) {
List<ProgressNode> children = currentNode.getChildren();
// 获取子流程当前审核人节点
ProgressNode subCurrentNode = children.get(children.size() - 1);
auditUserInfo = userInfoService.getById(Long.valueOf(subCurrentNode.getUserId()));
currentEmployeeCode = subCurrentNode.getUserId();
} else {
auditUserInfo = userInfoService.getById(Long.valueOf(currentNode.getUserId()));
currentEmployeeCode = currentNode.getUserId();
}

// 流程通过后,判断当前登录用户是不是最后一个审核人
@@ -376,7 +372,7 @@ public class TodoCenterManage {
throw new IllegalStateException("Unexpected value: " + projectStatus);
}
// 获取发送浙政钉工作通知必要信息
WorkNoticeInfo passWorkNoticeInfo2 = getSendWorkNoticeInfo(startUserInfo);
WorkNoticeInfo passWorkNoticeInfo2 = getSendWorkNoticeInfo(currentEmployeeCode);
String passMsg2 = String.format(PASS_MSG_TEMPLATE2, projectName, processDefinitionName);
passWorkNoticeInfo2.setMsg(passMsg2);
// 放入系统通知表中,保存记录
@@ -387,10 +383,10 @@ public class TodoCenterManage {
// 若有下一个审核人(当前节点的用户),
// 向其发送浙政钉工作通知:标题:审核任务 内容:【单位名称】的【项目名称】需要您审核。
// 获取发送浙政钉工作通知必要信息
if (Objects.isNull(auditUserInfo)) {
if (Objects.isNull(currentEmployeeCode)) {
throw new BizException("审核人信息不存在!");
}
WorkNoticeInfo sendWorkNoticeInfo = getSendWorkNoticeInfo(auditUserInfo);
WorkNoticeInfo sendWorkNoticeInfo = getSendWorkNoticeInfo(currentEmployeeCode);
String msg = String.format(PASS_MSG_TEMPLATE, sendWorkNoticeInfo.getOrganizationName(), projectName);
sendWorkNoticeInfo.setMsg(msg);
// 放入系统通知表中,保存记录
@@ -407,7 +403,7 @@ public class TodoCenterManage {
// 更新项目状态和流程状态
updateRejectProjectStatus(userId, declaredProject);
// 获取发送浙政钉工作通知必要信息
WorkNoticeInfo rejectWorkNoticeInfo = getSendWorkNoticeInfo(startUserInfo);
WorkNoticeInfo rejectWorkNoticeInfo = getSendWorkNoticeInfo(instance.getStartUserId());
String rejectMsg = String.format(REJECT_MSG_TEMPLATE, projectName, processDefinitionName);
rejectWorkNoticeInfo.setMsg(rejectMsg);
// 放入系统通知表中,保存记录
@@ -421,7 +417,7 @@ public class TodoCenterManage {
processTaskService.handleTask(param, employeeCode);
// 给项目创建人、流程发起人发送浙政钉工作通知:【项目名称】的【流程名称】被退回,请及时处理。
// 获取发送浙政钉工作通知必要信息
WorkNoticeInfo backWorkNoticeInfo = getSendWorkNoticeInfo(startUserInfo);
WorkNoticeInfo backWorkNoticeInfo = getSendWorkNoticeInfo(instance.getStartUserId());
String backMsg = String.format(BACK_MSG_TEMPLATE, projectName, processDefinitionName);
backWorkNoticeInfo.setMsg(backMsg);
// 放入系统通知表中,保存记录
@@ -432,7 +428,7 @@ public class TodoCenterManage {
// 撤回(流程发起人和当前流程审核人的前一个审核人操作)
case WITHDRAW:
// 登录用户是流程发起人,且是流程发起人撤回
if (employeeCode.equals(startUserId)
if (employeeCode.equals(instance.getStartUserId())
&& ProcessConstant.Field.ROOT.equals(param.getTaskId())) {
processTaskService.handleTask(param, employeeCode);
// 若是流程发起人点击撤回,项目回到上一个状态,需调用状态机更新项目状态,流程状态更新为审核通过
@@ -589,12 +585,16 @@ public class TodoCenterManage {
/**
* 获取发送浙政钉工作通知的信息
*
* @param auditUserInfo
* @param currentEmployeeCode
* @return com.ningdatech.pmapi.todocenter.bean.entity.WorkNoticeInfo
* @author CMM
* @since 2023/02/15 14:04
*/
public WorkNoticeInfo getSendWorkNoticeInfo(UserInfo auditUserInfo) {
public WorkNoticeInfo getSendWorkNoticeInfo(String currentEmployeeCode) {
UserInfo auditUserInfo = userInfoService.getOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getEmployeeCode,currentEmployeeCode).last("limit 1"));
if (Objects.isNull(auditUserInfo)) {
throw new BizException("该用户不存在!");
}
WorkNoticeInfo workNoticeInfo = new WorkNoticeInfo();
Long accountId = auditUserInfo.getAccountId();
if (Objects.isNull(accountId)) {


+ 7
- 6
pmapi/src/main/java/com/ningdatech/pmapi/user/service/impl/UserInfoServiceImpl.java View File

@@ -1,5 +1,6 @@
package com.ningdatech.pmapi.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ningdatech.basic.exception.BizException;
@@ -15,10 +16,7 @@ import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;

/**
@@ -68,10 +66,13 @@ public class UserInfoServiceImpl extends ServiceImpl<NdUserInfoMapper, UserInfo>
@Override
public Map<String, ProcessInstanceUserDto> getUserMapByEmployeeCode(Set<String> staterUsers) {
List<UserInfo> userInfos = userInfoMapper.selectList(Wrappers.lambdaQuery(UserInfo.class)
.eq(UserInfo::getEmployeeCode,staterUsers));
.in(UserInfo::getEmployeeCode,staterUsers));
if(CollUtil.isEmpty(userInfos)){
return Collections.emptyMap();
}
return userInfos.stream().map(u -> {
ProcessInstanceUserDto userInfo = new ProcessInstanceUserDto();
userInfo.setUserId(String.valueOf(u.getId()));
userInfo.setUserId(u.getEmployeeCode());
userInfo.setUserName(u.getRealName());
Long accountId = u.getAccountId();
if (Objects.isNull(accountId)) {


+ 4
- 5
pmapi/src/test/java/com/ningdatech/pmapi/todocenter/TodoCenterTest.java View File

@@ -85,10 +85,9 @@ public class TodoCenterTest extends AppTests {
////若没有指定属性名,则默认为方法名
//TaskExecutor taskExecutor = (TaskExecutor) ac.getBean("executor");

Long userId = 4L;
UserInfo auditUserInfo = userInfoService.getById(userId);
String userId = "4";
// 获取发送浙政钉工作通知必要信息
WorkNoticeInfo workNoticeInfo = todoCenterManage.getSendWorkNoticeInfo(auditUserInfo);
WorkNoticeInfo workNoticeInfo = todoCenterManage.getSendWorkNoticeInfo(userId);
// workNoticeInfo.setBizMsgId("1");
String msg = String.format(PASS_MSG_TEMPLATE, "发改委", "0223-02-测试项目");

@@ -125,9 +124,9 @@ public class TodoCenterTest extends AppTests {

@Test
public void sendWorkNoticeTest2(){
UserInfo userInfo = userInfoService.getById(4L);
String userId = "2";
// 获取发送浙政钉工作通知必要信息
WorkNoticeInfo passWorkNoticeInfo = todoCenterManage.getSendWorkNoticeInfo(userInfo);
WorkNoticeInfo passWorkNoticeInfo = todoCenterManage.getSendWorkNoticeInfo(userId);
String passMsg = String.format(PASS_MSG_TEMPLATE, passWorkNoticeInfo.getOrganizationName(), "测试项目0301-1");
passWorkNoticeInfo.setMsg(passMsg);
// 放入工作通知暂存表中,通过扫表异步发送


Loading…
Cancel
Save