diff --git a/hz-pm-api/pom.xml b/hz-pm-api/pom.xml index da123d9..06a8fa7 100644 --- a/hz-pm-api/pom.xml +++ b/hz-pm-api/pom.xml @@ -163,11 +163,6 @@ nd-cache-starter - com.ningdatech - nd-yxt-starter - 1.0.0 - - com.alibaba easyexcel-core diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/common/helper/MsgCallHelper.java b/hz-pm-api/src/main/java/com/hz/pm/api/common/helper/MsgCallHelper.java new file mode 100644 index 0000000..1f6e647 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/common/helper/MsgCallHelper.java @@ -0,0 +1,184 @@ +package com.hz.pm.api.common.helper; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.PhoneUtil; +import cn.hutool.core.util.StrUtil; +import com.hz.pm.api.external.MobileCallClient; +import com.hz.pm.api.external.MobileCallClient.ReplyCallDTO; +import com.hz.pm.api.external.MobileCallClient.SendCallDTO; +import com.hz.pm.api.external.model.dto.CallRetDTO; +import com.hz.pm.api.external.sms.MhSmsClient; +import com.hz.pm.api.external.sms.dto.SmsDTO; +import com.hz.pm.api.external.sms.vo.SmsReplyDTO; +import com.hz.pm.api.external.sms.vo.SmsSendDTO; +import com.hz.pm.api.sys.model.entity.MsgCallRecord; +import com.hz.pm.api.sys.model.enumeration.BizTypeEnum; +import com.hz.pm.api.sys.model.enumeration.SubmitTypeEnum; +import com.hz.pm.api.sys.service.IMsgCallRecordService; +import com.ningdatech.basic.exception.BizException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.View; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static com.hz.pm.api.sys.model.entity.MsgCallRecord.ReplyStatus; +import static com.hz.pm.api.sys.model.entity.MsgCallRecord.SendStatus; + +/** + *

+ * MsgCallHelper + *

+ * + * @author WendyYang + * @since 15:49 2024/4/25 + */ +@Component +@Slf4j +@RequiredArgsConstructor +public class MsgCallHelper { + + private final IMsgCallRecordService msgCallRecordService; + private final View error; + + /** + * 发送短信 + * + * @param phone 手机号 + * @param content 短信内容 + * @param bizType 业务类型 + * @return submitKey + **/ + public String sendMsg(String phone, String content, BizTypeEnum bizType) { + return sendMsg(Collections.singletonList(phone), content, bizType); + } + + /** + * 批量发送短信 + * + * @param phones 手机号 + * @param content 短信内容 + * @param bizType 业务类型 + * @return submitKey + **/ + public String sendMsg(Collection phones, String content, BizTypeEnum bizType) { + log.info("sendMsgCall:{},{}", phones, content); + Assert.notEmpty(phones, "手机号不能为空"); + phones.forEach(w -> { + if (!PhoneUtil.isMobile(w)) { + throw BizException.wrap("手机号格式错误:%s", w); + } + }); + SmsDTO ret = MhSmsClient.getClient().smsSend(content, phones); + if (!ret.isOk()) { + log.error("短信发送失败:{}", ret); + throw BizException.wrap("短信发送失败"); + } + SmsSendDTO data = ret.getData(); + List errorPhones = StrUtil.split(data.getErrorPhone(), ","); + List records = phones.stream().map(w -> { + MsgCallRecord mcr = new MsgCallRecord(); + mcr.setContent(content); + mcr.setReceivePhone(w); + mcr.setSubmitKey(data.getResult()); + mcr.setBizType(bizType); + mcr.setSubmitType(SubmitTypeEnum.SMS); + mcr.setSendStatus(errorPhones.contains(w) ? SendStatus.SUCCESS : SendStatus.FAILED); + buildReplyStatusBySend(mcr, bizType); + return mcr; + }).collect(Collectors.toList()); + msgCallRecordService.saveBatch(records); + return data.getResult(); + } + + private void buildReplyStatusBySend(MsgCallRecord mcr, BizTypeEnum bizType) { + if (SendStatus.SUCCESS.equals(mcr.getSendStatus())) { + switch (bizType) { + case EXPERT_INVITE: + mcr.setNeedReply(Boolean.TRUE); + mcr.setReplyStatus(ReplyStatus.WAITING); + break; + default: + mcr.setNeedReply(Boolean.FALSE); + break; + } + } + } + + /** + * 获取短信回复内容 + * + * @param submitKey submitKey + * @return 回复内容 + */ + public SmsReplyDTO smsReply(String submitKey) { + return MhSmsClient.getClient().smsReply(submitKey); + } + + /** + * 批量提交电话任务 + * + * @param phones 手机号 + * @param content 内容 + * @param bizType 业务类型 + * @return submitKey submitKey + */ + public String sendCall(Collection phones, String content, BizTypeEnum bizType) { + log.info("sendCall:{},{}", phones, content); + Assert.notEmpty(phones, "手机号不能为空"); + phones.forEach(phone -> { + if (!PhoneUtil.isMobile(phone)) { + throw BizException.wrap("手机号格式错误:%s", phone); + } + }); + CallRetDTO ret = MobileCallClient.getClient().sendCall(phones, content); + if (!ret.isOk()) { + log.error("电话拨打失败:{}", ret); + throw BizException.wrap("电话拨打失败"); + } + SendCallDTO data = ret.getData(); + List records = phones.stream().map(w -> { + MsgCallRecord mcr = new MsgCallRecord(); + mcr.setContent(content); + mcr.setReceivePhone(w); + mcr.setSubmitKey(data.getRequestId()); + mcr.setBizType(bizType); + mcr.setSubmitType(SubmitTypeEnum.CALL); + mcr.setSendStatus(SendStatus.SUCCESS); + buildReplyStatusBySend(mcr, bizType); + return mcr; + }).collect(Collectors.toList()); + msgCallRecordService.saveBatch(records); + return data.getRequestId(); + } + + /** + * 提交电话任务 + * + * @param phone 手机号 + * @param content 内容 + * @param bizType 业务类型 + * @return submitKey submitKey + */ + public String sendCall(String phone, String content, BizTypeEnum bizType) { + return sendCall(Collections.singletonList(phone), content, bizType); + } + + public List callReply(String submitKey) { + CallRetDTO> retCallReplies = MobileCallClient.getClient().replyCall(submitKey); + if (retCallReplies.isOk()) { + List replies = retCallReplies.getData(); + if (replies == null || replies.isEmpty()) { + return Collections.emptyList(); + } + return replies; + } + log.error("获取电话回执失败:{} {}", submitKey, retCallReplies); + throw BizException.wrap("获取电话回执失败"); + } + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/expert/service/impl/ExpertInfoServiceImpl.java b/hz-pm-api/src/main/java/com/hz/pm/api/expert/service/impl/ExpertInfoServiceImpl.java index 2b2e961..697504c 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/expert/service/impl/ExpertInfoServiceImpl.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/expert/service/impl/ExpertInfoServiceImpl.java @@ -2,8 +2,7 @@ package com.hz.pm.api.expert.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.PhoneUtil; +import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; @@ -24,8 +23,7 @@ import com.hz.pm.api.expert.model.bo.ExpertInfoSensitiveFieldCheckBO; import com.hz.pm.api.expert.model.cmd.*; import com.hz.pm.api.expert.model.dto.*; import com.hz.pm.api.expert.service.*; -import com.hz.pm.api.expert.utils.SensitiveModifySegmentUtils; -import com.hz.pm.api.meeting.helper.YxtClientHelper; +import com.hz.pm.api.expert.utils.SensitiveModifySegmentUtil; import com.hz.pm.api.meta.constant.ExpertDictTypeEnum; import com.hz.pm.api.meta.model.ExpertRegionInfo; import com.hz.pm.api.meta.model.entity.ExpertDictionary; @@ -36,19 +34,16 @@ import com.hz.pm.api.sys.model.entity.Role; import com.hz.pm.api.sys.model.entity.UserRole; import com.hz.pm.api.sys.service.IRoleService; import com.hz.pm.api.sys.service.IUserRoleService; -import com.hz.pm.api.user.model.enumeration.UserAvailableEnum; import com.hz.pm.api.user.model.entity.UserInfo; import com.hz.pm.api.user.model.enumeration.RoleEnum; -import com.hz.pm.api.user.security.model.UserInfoDetails; +import com.hz.pm.api.user.model.enumeration.UserAvailableEnum; import com.hz.pm.api.user.service.IUserInfoService; import com.hz.pm.api.user.util.LoginUserUtil; import com.ningdatech.basic.exception.BizException; import com.ningdatech.basic.util.CollUtils; -import com.ningdatech.yxt.utils.JSONUtils; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -57,10 +52,6 @@ import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; -import static com.hz.pm.api.expert.model.constant.ExpertAuditMsgTemplate.EXPERT_AUDIT_FAIL; -import static com.hz.pm.api.expert.model.constant.ExpertAuditMsgTemplate.EXPERT_AUDIT_PASS; -import static com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; - /** * @author liuxinxin * @date 2022/7/22 下午4:39 @@ -79,16 +70,12 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { private final IUserRoleService userRoleService; private final IExpertSensitiveInfoModifyDetailRecordService iExpertSensitiveInfoModifyDetailRecordService; private final IUserInfoService userInfoService; - private final YxtClientHelper yxtClientHelper; private final IExpertGovBusinessStripService expertGovBusinessStripService; - @Value("${login.url:}") - private String loginUrl; - /** * 用户第一次申请修改,仅在专家用户状态为applying 状态时调用 * - * @param cmd + * @param cmd \ */ @Override @Transactional(rollbackFor = Exception.class) @@ -365,8 +352,6 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { public void expertStorageDeal(ExpertStorageDealCmd cmd) { Long expertUserId = cmd.getExpertUserId(); ExpertUserFullInfo expertInfo = iExpertUserFullInfoService.getByUserId(expertUserId); - UserInfoDetails userDetail = LoginUserUtil.loginUserDetail(); - String content; if (Boolean.TRUE.equals(cmd.getApplyResult())) { // 修改专家状态为可用 // 账号启用 @@ -396,17 +381,8 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { userRoleService.save(saveUserRole); } } - content = String.format(EXPERT_AUDIT_PASS, expertInfo.getExpertName(), loginUrl, userDetail.getRealName(), userDetail.getMobile()); - } else { - content = String.format(EXPERT_AUDIT_FAIL, expertInfo.getExpertName(), userDetail.getRealName(), userDetail.getMobile()); } iExpertUserFullInfoService.saveOrUpdate(expertInfo); - if (PhoneUtil.isMobile(expertInfo.getPhoneNo())) { - SendSmsContext smsCtx = new SendSmsContext(); - smsCtx.setContent(content); - smsCtx.setReceiveNumber(expertInfo.getPhoneNo()); - yxtClientHelper.sendSms(smsCtx); - } } @@ -631,8 +607,8 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { * 创建层级变更审核 * * @param expertRegionFieldSegmentList \ - * @param originalExpertUserFullInfo \ - * @param modifyApplyExtraInfo \ + * @param originalExpertUserFullInfo \ + * @param modifyApplyExtraInfo \ */ private Long createExpertRegionModifyApply(List expertRegionFieldSegmentList , ExpertUserFullInfo originalExpertUserFullInfo, ModifyApplyExtraInfoDTO modifyApplyExtraInfo) { @@ -674,7 +650,7 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { Boolean applyResult = expertInfoModifyApplyDealCmd.getApplyResult(); Long expertUserId = expertInfoModifyApplyDealCmd.getExpertUserId(); Long applyId = expertInfoModifyApplyDealCmd.getApplyId(); - if (applyResult) { + if (Boolean.TRUE.equals(applyResult)) { ExpertUserFullInfo originalExpertUserFullInfo = iExpertUserFullInfoService.getByUserId(expertUserId); LambdaQueryWrapper recordQuery = Wrappers.lambdaQuery(ExpertSensitiveInfoModifyDetailRecord.class) @@ -685,7 +661,7 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { if (StringUtils.isNotEmpty(modifyJson)) { modifyJson = GzipUtil.unzip(modifyJson); } - List sensitiveModifySegments = JSONUtils.parseArray(modifyJson, SensitiveModifySegment.class); + List sensitiveModifySegments = JSONUtil.toList(modifyJson, SensitiveModifySegment.class); Map collectMap = sensitiveModifySegments .stream().collect(Collectors.toMap(SensitiveModifySegment::getFieldName, Function.identity())); @@ -720,7 +696,7 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { case title_level: { SensitiveModifySegment segment = collectMap.get(sensitiveFieldEnum); if (Objects.nonNull(segment)) { - List dictionaryFieldInfoList = SensitiveModifySegmentUtils.getDictionaryFieldInfoListApply(segment); + List dictionaryFieldInfoList = SensitiveModifySegmentUtil.getDictionaryFieldInfoListApply(segment); updateExpertDictionaryList(expertUserId, dictionaryFieldInfoList, sensitiveFieldEnum.getKey()); } } @@ -742,7 +718,7 @@ public class ExpertInfoServiceImpl implements ExpertInfoService { case other: { SensitiveModifySegment segment = collectMap.get(sensitiveFieldEnum); if (Objects.nonNull(segment)) { - List tagFieldInfoList = SensitiveModifySegmentUtils.getTagFieldInfoListApply(segment); + List tagFieldInfoList = SensitiveModifySegmentUtil.getTagFieldInfoListApply(segment); updateExpertTagList(expertUserId, tagFieldInfoList, sensitiveFieldEnum.getKey()); } } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/expert/utils/SensitiveModifySegmentUtils.java b/hz-pm-api/src/main/java/com/hz/pm/api/expert/utils/SensitiveModifySegmentUtil.java similarity index 79% rename from hz-pm-api/src/main/java/com/hz/pm/api/expert/utils/SensitiveModifySegmentUtils.java rename to hz-pm-api/src/main/java/com/hz/pm/api/expert/utils/SensitiveModifySegmentUtil.java index ed56edb..6f9959e 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/expert/utils/SensitiveModifySegmentUtils.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/expert/utils/SensitiveModifySegmentUtil.java @@ -1,11 +1,11 @@ package com.hz.pm.api.expert.utils; -import com.ningdatech.basic.exception.BizException; +import cn.hutool.json.JSONUtil; import com.hz.pm.api.expert.model.DictFieldInfoDTO; import com.hz.pm.api.expert.model.SensitiveModifySegment; import com.hz.pm.api.expert.model.TagFieldInfo; -import com.ningdatech.yxt.utils.JSONUtils; +import com.ningdatech.basic.exception.BizException; import java.util.ArrayList; import java.util.List; @@ -16,9 +16,9 @@ import java.util.Objects; * @date 2022/8/24 下午3:36 */ -public class SensitiveModifySegmentUtils { +public class SensitiveModifySegmentUtil { - private SensitiveModifySegmentUtils() { + private SensitiveModifySegmentUtil() { } public static List getTagFieldInfoListApply(SensitiveModifySegment segment) { @@ -28,7 +28,7 @@ public class SensitiveModifySegmentUtils { case industry_sector: case other: if (Objects.nonNull(segment.getApply())) { - return JSONUtils.parseArray(segment.getApply().toString(), TagFieldInfo.class); + return JSONUtil.toList(segment.getApply().toString(), TagFieldInfo.class); } return new ArrayList<>(); default: @@ -42,7 +42,7 @@ public class SensitiveModifySegmentUtils { case administrative_rank: case title_level: { if (Objects.nonNull(segment.getApply())) { - return JSONUtils.parseArray(segment.getApply().toString(), DictFieldInfoDTO.class); + return JSONUtil.toList(segment.getApply().toString(), DictFieldInfoDTO.class); } return new ArrayList<>(); } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/MhFileClient.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/MhFileClient.java index 4300df1..f83283e 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/external/MhFileClient.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/MhFileClient.java @@ -88,7 +88,7 @@ public class MhFileClient { } ClassPathResource resource = new ClassPathResource("/response/ret-xcfhx-report-file.pdf"); File destFile = new File(FileUtil.getTmpDirPath() + RandomUtil.randomString(20) + ".pdf"); - return FileUtil.copy(resource.getFile(), destFile, true); + return FileUtil.writeFromStream(resource.getStream(), destFile, true); } } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/MobileCallClient.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/MobileCallClient.java new file mode 100644 index 0000000..6dcf027 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/MobileCallClient.java @@ -0,0 +1,105 @@ +package com.hz.pm.api.external; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.TypeReference; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.hz.pm.api.external.model.dto.CallRetDTO; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + *

+ * MobileCallClient + *

+ * + * @author WendyYang + * @since 17:20 2024/4/25 + */ +@Slf4j +public class MobileCallClient { + + @Data + public static class SendCallDTO { + + private String requestId; + + } + + @Data + public static class ReplyCallDTO { + + private String remoteNumber; + + private String dtmf; + + private Date sendDate; + + } + + //================================================================================================================== + + private static final Object LOCK = new Object(); + + private static final String CALL_HOST_KEY = "mobile-call.host"; + private static final String CALL_OPEN_KEY = "mobile-call.open"; + + //================================================================================================================== + + private static volatile MobileCallClient client; + + private static volatile boolean mobileCallOpen = Boolean.TRUE; + + public static MobileCallClient getClient() { + if (mobileCallOpen && client == null) { + synchronized (LOCK) { + if (client == null) { + Environment environment = SpringUtil.getBean(Environment.class); + mobileCallOpen = environment.getProperty(CALL_OPEN_KEY, Boolean.class, Boolean.TRUE); + if (mobileCallOpen) { + String callHost = environment.getProperty(CALL_HOST_KEY); + Assert.notBlank(callHost, "语音服务地址不能为空"); + client = new MobileCallClient(callHost); + } + } + } + } + Assert.isTrue(mobileCallOpen, "语音服务未开启"); + return client; + } + + private final String callHost; + + private MobileCallClient(String callHost) { + this.callHost = callHost; + } + + + private static final String MOBILE_CALL_URL = "/send_batch_voice_sms"; + private static final String MOBILE_REPLY_URL = "/query_voice_sms"; + + public CallRetDTO sendCall(Collection phones, String content) { + String url = callHost + MOBILE_CALL_URL; + JSONObject bodyObj = new JSONObject(); + bodyObj.set("remote_numbers", phones); + bodyObj.set("content", content); + String retStr = HttpUtil.post(url, bodyObj.toString()); + return JSONUtil.toBean(retStr, new TypeReference>() { + }, false); + } + + public CallRetDTO> replyCall(String requestId) { + String url = callHost + MOBILE_REPLY_URL + "?request_id=" + requestId; + String retStr = HttpUtil.get(url); + return JSONUtil.toBean(retStr, new TypeReference>>() { + }, false); + } + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/model/dto/CallRetDTO.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/model/dto/CallRetDTO.java new file mode 100644 index 0000000..3fcfb59 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/model/dto/CallRetDTO.java @@ -0,0 +1,24 @@ +package com.hz.pm.api.external.model.dto; + +import lombok.Data; + +/** + *

+ * CallRetDTO + *

+ * + * @author WendyYang + * @since 17:38 2024/4/25 + */ +@Data +public class CallRetDTO { + + private Boolean success; + + private T data; + + public boolean isOk() { + return Boolean.TRUE.equals(success); + } + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/MhSmsClient.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/MhSmsClient.java new file mode 100644 index 0000000..e9dc8a2 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/MhSmsClient.java @@ -0,0 +1,147 @@ +package com.hz.pm.api.external.sms; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.TypeReference; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.hz.pm.api.external.sms.dto.SmsDTO; +import com.hz.pm.api.external.sms.vo.SmsReceipt; +import com.hz.pm.api.external.sms.vo.SmsReplyDTO; +import com.hz.pm.api.external.sms.vo.SmsSendDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + *

+ * MhSmsClient + *

+ * + * @author WendyYang + * @since 16:38 2024/4/25 + */ +@Slf4j +public class MhSmsClient { + + private static volatile MhSmsClient client; + + private static volatile boolean smsSendOpen = Boolean.TRUE; + + private static final String SMS_HOST_KEY = "sms-client.host"; + private static final String SMS_OPEN_KEY = "sms-client.open"; + + private static final Object LOCK = new Object(); + + public static MhSmsClient getClient() { + if (smsSendOpen && client == null) { + synchronized (LOCK) { + if (client == null) { + Environment environment = SpringUtil.getBean(Environment.class); + smsSendOpen = environment.getProperty(SMS_OPEN_KEY, Boolean.class, Boolean.TRUE); + if (smsSendOpen) { + String smsHost = environment.getProperty(SMS_HOST_KEY); + Assert.notBlank(smsHost, "短信服务地址不能为空"); + client = new MhSmsClient(smsHost); + } + } + } + } + Assert.isTrue(smsSendOpen, "短信服务未开启"); + return client; + } + + //================================================================================================================== + + private MhSmsClient(String smsUrl) { + this.smsUrl = smsUrl; + } + + /** + * 短信服务地址前缀 + */ + private final String smsUrl; + /** + * 获取短信回复结果 + */ + private static final String SMS_REPLY = "/sms/reply"; + /** + * 短信发送 + */ + private static final String SMS_SEND = "/sms/send"; + /** + * 短信状态 + */ + private static final String SMS_RECEIPT = "/sms/status"; + + /** + * 短信回复 + * + * @param result \ + * @return \ + */ + public SmsReplyDTO smsReply(String result) { + String refreshUrl = smsUrl + SMS_REPLY; + Map map = new HashMap<>(); + map.put("result", result); + String responseResult = HttpUtil.post(refreshUrl, map); + JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); + String fileData = responseJson.getString("data"); + return JSONUtil.toBean(fileData, new TypeReference() { + }, false); + } + + /** + * 短信发送 + * + * @return \ + */ + public SmsDTO smsSend(String content, Collection phones) { + String phonesSplit = String.join(",", phones); + String refreshUrl = smsUrl + SMS_SEND; + Map map = new HashMap<>(); + map.put("content", content); + map.put("phones", phonesSplit); + String responseResult = HttpUtil.post(refreshUrl, JSON.toJSONString(map)); + return JSONUtil.toBean(responseResult, new TypeReference>() { + }, false); + } + + /** + * 短信发送 + * + * @return \ + */ + public SmsDTO smsSend(String content, String phone) { + return smsSend(content, Collections.singletonList(phone)); + + } + + /** + * 短信回执 + * + * @param result \ + * @return \ + */ + public SmsReceipt smsReceipt(String result) { + String refreshUrl = smsUrl + SMS_RECEIPT; + HashMap map = new HashMap<>(); + map.put("result", result); + String responseResult = HttpUtil.post(refreshUrl, map); + JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); + String fileData = responseJson.getString("data"); + JSONObject jsonObject = JSON.parseObject(fileData); + Map dataMap = jsonObject.getInnerMap(); + if (dataMap.get("result") instanceof Boolean) { + return SmsReceipt.builder().error(dataMap.get("error").toString()).build(); + } + return JSONObject.parseObject(fileData, SmsReceipt.class); + } + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/SmsServiceClient.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/SmsServiceClient.java deleted file mode 100644 index ed5a94c..0000000 --- a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/SmsServiceClient.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.hz.pm.api.external.sms; - -import cn.hutool.core.lang.TypeReference; -import cn.hutool.http.HttpUtil; -import cn.hutool.json.JSONUtil; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.hz.pm.api.external.sms.dto.SmsDTO; -import com.hz.pm.api.external.sms.vo.SmsReceipt; -import com.hz.pm.api.external.sms.vo.SmsReplyResponse; -import com.hz.pm.api.external.sms.vo.SmsSendResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Slf4j -@Component -@RequiredArgsConstructor -public class SmsServiceClient { - - /** - * 短信服务 - */ - @Value("${sms.client-url:}") - private String smsUrl; - - public static final String SMS_REPLY = "/sms/reply"; - public static final String SMS_SEND = "/sms/send"; - public static final String SMS_RECEIPT = "/sms/status"; - /** - * 短信回复 - * @param result - * @return - */ - public SmsReplyResponse smsReply(String result) { - String refreshUrl = smsUrl + SMS_REPLY; - HashMap map = new HashMap<>(); - map.put("result",result); - String responseResult = HttpUtil.post(refreshUrl, map); - JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); - String fileData = responseJson.getString("data"); - return JSONUtil.toBean(fileData, new TypeReference() { - }, false); - } - - /** - * 短信发送 - * @return - */ - public SmsDTO smsSend(String content, List phones) { - String phonesSplit = String.join(",", phones); - String refreshUrl = smsUrl + SMS_SEND; - Map map = new HashMap<>(); - map.put("content",content); - map.put("phones",phonesSplit); - String responseResult = HttpUtil.post(refreshUrl, JSON.toJSONString(map)); - return JSONUtil.toBean(responseResult, new TypeReference>() { - }, false); - } - - /** - * 短信回执 - * @param result - * @return - */ - public SmsReceipt smsReceipt(String result) { - String refreshUrl = smsUrl + SMS_RECEIPT; - HashMap map = new HashMap<>(); - map.put("result",result); - String responseResult = HttpUtil.post(refreshUrl, map); - JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); - String fileData = responseJson.getString("data"); - JSONObject jsonObject = JSON.parseObject(fileData); - Map dataMap = jsonObject.getInnerMap(); - if(dataMap.get("result") instanceof Boolean){ - return SmsReceipt.builder().error(dataMap.get("error").toString()).build(); - } - return JSONObject.parseObject(fileData, SmsReceipt.class); - } - -} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/dto/SmsDTO.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/dto/SmsDTO.java index 93005c5..9ff241a 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/dto/SmsDTO.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/dto/SmsDTO.java @@ -2,13 +2,21 @@ package com.hz.pm.api.external.sms.dto; import lombok.Data; +/** + *

+ * SmsDTO + *

+ * + * @author WendyYang + * @since 15:56 2024/4/25 + */ @Data public class SmsDTO { /** * 成功状态码 */ - private static final int OK_CODE = 200; + private static final Integer OK_CODE = 200; /** * 失败状态码(未找到资源) */ @@ -19,4 +27,9 @@ public class SmsDTO { private String msg; private T data; + + public boolean isOk() { + return OK_CODE.equals(this.getCode()); + } + } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsReplyResponse.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsReplyDTO.java similarity index 84% rename from hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsReplyResponse.java rename to hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsReplyDTO.java index db6efe3..37ea239 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsReplyResponse.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsReplyDTO.java @@ -6,9 +6,17 @@ import lombok.Data; import java.util.List; +/** + *

+ * SmsReplyDTO + *

+ * + * @author WendyYang + * @since 17:05 2024/4/25 + */ @Data @ApiModel("短信回复响应内容") -public class SmsReplyResponse { +public class SmsReplyDTO { @ApiModelProperty("参数校验失败的错误提示") private String error; diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsSendResponse.java b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsSendDTO.java similarity index 72% rename from hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsSendResponse.java rename to hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsSendDTO.java index 42bfade..15e223c 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsSendResponse.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/external/sms/vo/SmsSendDTO.java @@ -2,8 +2,16 @@ package com.hz.pm.api.external.sms.vo; import lombok.Data; +/** + *

+ * SmsSendDTO + *

+ * + * @author WendyYang + * @since 16:56 2024/4/25 + */ @Data -public class SmsSendResponse { +public class SmsSendDTO { //发送失败的手机号 private String errorPhone; diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/leave/manage/LeaveManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/leave/manage/LeaveManage.java index e993f43..57699f4 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/leave/manage/LeaveManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/leave/manage/LeaveManage.java @@ -32,7 +32,7 @@ import com.hz.pm.api.meeting.entity.domain.MeetingExpert; import com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum; import com.hz.pm.api.meeting.entity.enumeration.ExpertInviteTypeEnum; import com.hz.pm.api.meeting.entity.enumeration.MeetingStatusEnum; -import com.hz.pm.api.meeting.helper.MeetingCallOrMsgHelper; +import com.hz.pm.api.meeting.helper.MeetingNotifyHelper; import com.hz.pm.api.meeting.service.IMeetingExpertService; import com.hz.pm.api.meeting.service.IMeetingService; import com.hz.pm.api.meeting.task.ExpertRandomInviteTask; @@ -75,7 +75,7 @@ public class LeaveManage { private final IExpertLeaveDetailService leaveDetailService; private final IExpertMetaApplyService metaApplyService; private final FileService fileService; - private final MeetingCallOrMsgHelper meetingCallOrMsgHelper; + private final MeetingNotifyHelper meetingNotifyHelper; private static final int HOURS_BEFORE_MEETING = 2; @@ -202,7 +202,7 @@ public class LeaveManage { .eq(Meeting::getId, meeting.getId()); meetingService.update(mUpdate); } - meetingCallOrMsgHelper.sendExpertLeaveMsg(expert, meeting); + meetingNotifyHelper.sendExpertLeaveMsg(expert, meeting); // 临时请假无需审核 leave.setAuditId(0L); leave.setStatus(LeaveStatusEnum.PASSED.getCode()); diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/controller/ExpertDashboardController.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/controller/ExpertDashboardController.java index 434fcf4..8e761c7 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/controller/ExpertDashboardController.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/controller/ExpertDashboardController.java @@ -1,10 +1,10 @@ package com.hz.pm.api.meeting.controller; -import com.hz.pm.api.external.sms.SmsServiceClient; +import com.hz.pm.api.external.sms.MhSmsClient; import com.hz.pm.api.external.sms.dto.SmsDTO; import com.hz.pm.api.external.sms.vo.SmsReceipt; -import com.hz.pm.api.external.sms.vo.SmsReplyResponse; -import com.hz.pm.api.external.sms.vo.SmsSendResponse; +import com.hz.pm.api.external.sms.vo.SmsReplyDTO; +import com.hz.pm.api.external.sms.vo.SmsSendDTO; import com.hz.pm.api.meeting.entity.req.MeetingCalenderReq; import com.hz.pm.api.meeting.entity.vo.*; import com.hz.pm.api.meeting.manage.DashboardManage; @@ -39,24 +39,22 @@ public class ExpertDashboardController { private final DashboardManage dashboardManage; - private final SmsServiceClient smsServiceClient; - @ApiOperation("短信发送") @GetMapping("/sendSms") - public SmsDTO test(String phone) { - return smsServiceClient.smsSend(VoiceSmsTemplateConst.EXPERT_INVITE, Collections.singletonList(phone)); + public SmsDTO test(String phone) { + return MhSmsClient.getClient().smsSend(VoiceSmsTemplateConst.EXPERT_INVITE, Collections.singletonList(phone)); } @ApiOperation("短信回复") @GetMapping("/aplySms") - public SmsReplyResponse smsReply(String uuid) { - return smsServiceClient.smsReply(uuid); + public SmsReplyDTO smsReply(String uuid) { + return MhSmsClient.getClient().smsReply(uuid); } @ApiOperation("短信回执") @GetMapping("/status") public SmsReceipt smsReceipt(String uuid) { - return smsServiceClient.smsReceipt(uuid); + return MhSmsClient.getClient().smsReceipt(uuid); } @ApiOperation("会议日历") diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java index 0ca760b..3088f10 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java @@ -26,6 +26,7 @@ public class MeetingExpert implements Serializable { @Tolerate public MeetingExpert() { + // 默认无参构造 } private static final long serialVersionUID = 1L; @@ -61,11 +62,9 @@ public class MeetingExpert implements Serializable { @ApiModelProperty("是否已确认名单") private Boolean confirmedRoster; + @ApiModelProperty("提交标识") private String submitKey; - @ApiModelProperty("短信发送返回的uuid") - private String smsUuid; - @ApiModelProperty("通知方式:1电话 2短信") private String notifyWay; diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/MeetingCallOrMsgHelper.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/MeetingNotifyHelper.java similarity index 74% rename from hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/MeetingCallOrMsgHelper.java rename to hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/MeetingNotifyHelper.java index 5c4ed30..221fad8 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/MeetingCallOrMsgHelper.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/MeetingNotifyHelper.java @@ -3,37 +3,34 @@ package com.hz.pm.api.meeting.helper; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hz.pm.api.common.helper.MsgCallHelper; import com.hz.pm.api.meeting.constant.MeetingMsgTemplateConst; import com.hz.pm.api.meeting.entity.domain.Meeting; import com.hz.pm.api.meeting.entity.domain.MeetingExpert; import com.hz.pm.api.meeting.entity.enumeration.ExpertInviteTypeEnum; import com.hz.pm.api.meeting.entity.enumeration.ExpertNotifyTypeEnum; import com.hz.pm.api.meeting.entity.enumeration.MeetingReviewTypeEnum; -import com.hz.pm.api.meeting.task.ExpertCallResultRewriteTask; import com.hz.pm.api.organization.model.entity.DingEmployeeInfo; import com.hz.pm.api.organization.model.entity.DingOrganization; import com.hz.pm.api.organization.service.IDingEmployeeInfoService; import com.hz.pm.api.organization.service.IDingOrganizationService; +import com.hz.pm.api.sms.constant.VoiceSmsTemplateConst; import com.hz.pm.api.sms.utils.DateUtil; import com.hz.pm.api.staging.enums.MsgTypeEnum; import com.hz.pm.api.staging.service.INdWorkNoticeStagingService; import com.hz.pm.api.sys.model.entity.Notify; +import com.hz.pm.api.sys.model.enumeration.BizTypeEnum; import com.hz.pm.api.sys.service.INotifyService; import com.hz.pm.api.todocenter.bean.entity.WorkNoticeInfo; import com.hz.pm.api.user.model.entity.UserInfo; import com.hz.pm.api.user.service.IUserInfoService; import com.ningdatech.basic.util.CollUtils; -import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import java.util.*; /** *

@@ -45,15 +42,14 @@ import java.util.stream.Collectors; **/ @Component @AllArgsConstructor -public class MeetingCallOrMsgHelper { +public class MeetingNotifyHelper { private final IUserInfoService userInfoService; - private final YxtClientHelper yxtClientHelper; + private final MsgCallHelper msgCallHelper; private final INdWorkNoticeStagingService workNoticeStagingService; private final IDingEmployeeInfoService dingEmployeeInfoService; private final IDingOrganizationService dingOrganizationService; private final INotifyService notifyService; - private final ExpertCallResultRewriteTask expertCallResultRewriteTask; private static String officialTime(LocalDateTime time) { return time.format(DateUtil.DTF_YMD_HM); @@ -100,10 +96,7 @@ public class MeetingCallOrMsgHelper { UserInfo info = userInfoService.getById(userId); String msgContent = String.format(MeetingMsgTemplateConst.INVITE_END, meetingName); // 音信通消息 - SendSmsContext yxtContent = new SendSmsContext(); - yxtContent.setContent(msgContent); - yxtContent.setReceiveNumber(info.getMobile()); - yxtClientHelper.sendSms(yxtContent); + msgCallHelper.sendMsg(info.getMobile(), msgContent, BizTypeEnum.EXPERT_INVITE_STOP); // 发送工作通知 if (info.getAccountId() != null) { WorkNoticeInfo swn = getSendWorkNoticeInfo(info.getAccountId()); @@ -124,7 +117,6 @@ public class MeetingCallOrMsgHelper { String sTime = officialTime(meeting.getStartTime()); String eTime = officialTime(meeting.getEndTime()); String meetingTime = sTime + "至" + eTime; - List yxtContents = new ArrayList<>(); List notifies = new ArrayList<>(); List workingNotices = new ArrayList<>(); experts.forEach(w -> { @@ -132,10 +124,7 @@ public class MeetingCallOrMsgHelper { w.getExpertName(), officialTime(w.getCreateOn()), meetingTime, meeting.getMeetingAddress(), meeting.getConnecter(), meeting.getContact()); // 音信通消息 - SendSmsContext yxtContent = new SendSmsContext(); - yxtContent.setContent(msgContent); - yxtContent.setReceiveNumber(w.getMobile()); - yxtContents.add(yxtContent); + msgCallHelper.sendMsg(w.getMobile(), msgContent, BizTypeEnum.MEETING_ROSTER_CONFIRM); UserInfo info = userMap.get(w.getExpertId()); // 发送工作通知 if (info.getAccountId() != null) { @@ -149,7 +138,6 @@ public class MeetingCallOrMsgHelper { } }); notifyService.saveBatch(notifies); - yxtClientHelper.sendSms(yxtContents); workNoticeStagingService.addByWorkNotice(workingNotices, MsgTypeEnum.EXPERT_REVIEW); } @@ -157,15 +145,11 @@ public class MeetingCallOrMsgHelper { public void sendCancelMeetingMsg(List experts, Meeting meeting) { String startTime = officialTime(meeting.getStartTime()); String meetingType = MeetingReviewTypeEnum.getValue(meeting.getType()); - List contexts = experts.stream().map(w -> { + for (MeetingExpert expert : experts) { String content = String.format(MeetingMsgTemplateConst.MEETING_CANCEL, - w.getExpertName(), startTime, meetingType, meeting.getContact()); - SendSmsContext context = new SendSmsContext(); - context.setReceiveNumber(w.getMobile()); - context.setContent(content); - return context; - }).collect(Collectors.toList()); - yxtClientHelper.sendSms(contexts); + expert.getExpertName(), startTime, meetingType, meeting.getContact()); + msgCallHelper.sendMsg(expert.getMobile(), content, BizTypeEnum.MEETING_CANCEL); + } } /** @@ -175,27 +159,36 @@ public class MeetingCallOrMsgHelper { * @param experts 待通知专家 * @author WendyYang **/ -// public void callExpertByMeeting(Meeting meeting, List experts) { -// String voiceContent = String.format(VoiceSmsTemplateConst.EXPERT_INVITE, -// meeting.getHoldOrg(), meeting.getName(), officialTime(meeting.getStartTime()), -// meeting.getMeetingAddress()); -// List callContexts = CollUtils.convert(experts, w -> { -// SubmitTaskCallContext context = new SubmitTaskCallContext(); -// context.setContent(voiceContent); -// context.setReceiveNumber(w.getMobile()); -// return context; -// }); -// String submitKey = yxtClientHelper.submitCallTask(callContexts); -// experts.forEach(w -> w.setSubmitKey(submitKey)); -// } - - public void smsOrCallExpertByMeeting(Meeting meeting, List experts) { - if(ExpertNotifyTypeEnum.CALL.eq(meeting.getNotifyWay())){ - // todo 电话通知 - }else if(ExpertNotifyTypeEnum.SMS.eq(meeting.getNotifyWay())){ - // 短信通知 - String smsUuid = expertCallResultRewriteTask.sendExpertSms(experts, meeting); - experts.forEach(w -> w.setSmsUuid(smsUuid)); + private void callExperts(Meeting meeting, List experts) { + String content = String.format(VoiceSmsTemplateConst.EXPERT_INVITE, + meeting.getHoldOrg(), meeting.getName(), officialTime(meeting.getStartTime()), + meeting.getMeetingAddress()); + Set phones = CollUtils.fieldSet(experts, MeetingExpert::getMobile); + String submitKey = msgCallHelper.sendCall(phones, content, BizTypeEnum.EXPERT_INVITE); + experts.forEach(w -> w.setSubmitKey(submitKey)); + } + + /** + * 发送专家短信任务 + * + * @param experts 专家 + * @param meeting 会议 + */ + private void smsExperts(Meeting meeting, List experts) { + String content = String.format(VoiceSmsTemplateConst.EXPERT_INVITE, + meeting.getHoldOrg(), meeting.getName(), officialTime(meeting.getStartTime()), + meeting.getMeetingAddress()); + Set phones = CollUtils.fieldSet(experts, MeetingExpert::getMobile); + String submitKey = msgCallHelper.sendMsg(phones, content, BizTypeEnum.EXPERT_INVITE); + // 短信发送成功返回的UUID + experts.forEach(w -> w.setSubmitKey(submitKey)); + } + + public void notifyExperts(Meeting meeting, List experts) { + if (ExpertNotifyTypeEnum.CALL.eq(meeting.getNotifyWay())) { + callExperts(meeting, experts); + } else if (ExpertNotifyTypeEnum.SMS.eq(meeting.getNotifyWay())) { + smsExperts(meeting, experts); } } @@ -209,10 +202,7 @@ public class MeetingCallOrMsgHelper { msgContent = String.format(MeetingMsgTemplateConst.EXPERT_LEAVE_APPOINT, meeting.getName(), expert.getExpertName()); } UserInfo info = userInfoService.getById(userId); - SendSmsContext yxtContent = new SendSmsContext(); - yxtContent.setContent(msgContent); - yxtContent.setReceiveNumber(info.getMobile()); - yxtClientHelper.sendSms(yxtContent); + msgCallHelper.sendMsg(info.getMobile(), msgContent, BizTypeEnum.EXPERT_LEAVE); // 发送工作通知 if (info.getAccountId() != null) { WorkNoticeInfo swn = getSendWorkNoticeInfo(info.getAccountId()); diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/SmsOrCallClient.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/SmsOrCallClient.java deleted file mode 100644 index a257551..0000000 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/SmsOrCallClient.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.hz.pm.api.meeting.helper; - -import com.alibaba.fastjson.JSONObject; -import com.hz.pm.api.external.sms.SmsServiceClient; -import com.hz.pm.api.external.sms.dto.SmsDTO; -import com.hz.pm.api.external.sms.vo.SmsSendResponse; -import com.hz.pm.api.meeting.entity.domain.MeetingExpertSms; -import com.hz.pm.api.meeting.mapper.MeetingExpertSmsMapper; -import com.ningdatech.yxt.model.cmd.SendSmsCmd; -import com.ningdatech.yxt.model.cmd.SubmitTaskCallCmd; -import com.ningdatech.yxt.model.cmd.SubmitTaskCallResponse; -import com.ningdatech.yxt.model.response.SendSmsResponse; -import lombok.AllArgsConstructor; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; -import java.util.Arrays; - -/** - * @author wangrenkang - * @date 2024-02-22 16:39:35 - */ -@Component -@AllArgsConstructor -@Primary -public class SmsOrCallClient implements com.ningdatech.yxt.client.YxtClient { - - private final SmsServiceClient smsServiceClient; - private final MeetingExpertSmsMapper meetingExpertSmsMapper; - - /** - * 发送短信 - * @param sendSmsCmd 短信内容,短信手机号 - * @return - */ - @Override - public SendSmsResponse submitSmsTask(SendSmsCmd sendSmsCmd) { - sendSmsCmd.getContextList().forEach(sms ->{ - SmsDTO smsSendResponseSmsDto = smsServiceClient.smsSend(sms.getContent(), Arrays.asList(sms.getReceiveNumber())); - String resultUuid = smsSendResponseSmsDto.getData().getResult(); - // 保存短信发送记录 - MeetingExpertSms meetingExpertSms = MeetingExpertSms.builder() - .createOn(LocalDateTime.now()) - .smsUuid(resultUuid) - .content(sms.getContent()) - .phones(sms.getReceiveNumber()) - .smsResult(smsSendResponseSmsDto.toString()) - .build(); - meetingExpertSmsMapper.insert(meetingExpertSms); - }); - return null; - } - - @Override - public SubmitTaskCallResponse submitTaskCall(SubmitTaskCallCmd submitTaskCallCmd) { - return null; - } - - @Override - public JSONObject getSentResultSms(String transactionId) { - return null; - } - - @Override - public JSONObject getSentResultCall(String transactionId) { - return null; - } -} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/YxtClientHelper.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/YxtClientHelper.java deleted file mode 100644 index 4d8d163..0000000 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/helper/YxtClientHelper.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.hz.pm.api.meeting.helper; - -import com.ningdatech.yxt.client.YxtClient; -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; - -/** - *

- * YxtCallOrSmsHelper - *

- * - * @author WendyYang - * @since 23:59 2022/8/31 - */ -@Component -@AllArgsConstructor -public class YxtClientHelper { - - private final YxtClient yxtClient; - - public String submitCallTask(List callContexts) { - SubmitTaskCallResponse callResponse = yxtClient.submitTaskCall(of(callContexts)); - return callResponse.getSubmitKey(); - } - - public void sendSms(List smsList) { - SendSmsCmd cmd = new SendSmsCmd(); - cmd.setContextList(smsList); -// cmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); - yxtClient.submitSmsTask(cmd); - } - - public void sendSms(SendSmsContext sms) { - sendSms(Collections.singletonList(sms)); - } - -} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/ExpertInviteManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/ExpertInviteManage.java index 73ed55a..d5514ff 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/ExpertInviteManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/ExpertInviteManage.java @@ -20,7 +20,7 @@ import com.hz.pm.api.meeting.entity.enumeration.AvoidTypeEnum; import com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum; import com.hz.pm.api.meeting.entity.enumeration.MeetingStatusEnum; import com.hz.pm.api.meeting.helper.ExpertInviteHelper; -import com.hz.pm.api.meeting.helper.MeetingCallOrMsgHelper; +import com.hz.pm.api.meeting.helper.MeetingNotifyHelper; import com.hz.pm.api.meeting.service.IExpertInviteRuleService; import com.hz.pm.api.meeting.service.IMeetingExpertService; import com.hz.pm.api.meeting.service.IMeetingService; @@ -68,7 +68,7 @@ public class ExpertInviteManage { private final IExpertUserFullInfoService expertUserFullInfoService; private final IMeetingService meetingService; private final IExpertAvoidCompanyService expertAvoidCompanyService; - private final MeetingCallOrMsgHelper meetingCallOrMsgHelper; + private final MeetingNotifyHelper meetingNotifyHelper; private final IExpertGovBusinessStripService expertGovBusinessStripService; @Value("#{randomInviteProperties.recentMeetingCount}") @@ -576,9 +576,7 @@ public class ExpertInviteManage { expertInserts.add(expert); }); } -// meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertInserts); - // 短信或电话提醒 - meetingCallOrMsgHelper.smsOrCallExpertByMeeting(meeting, expertInserts); + meetingNotifyHelper.notifyExperts(meeting, expertInserts); } meetingExpertService.saveBatch(expertInserts); } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/MeetingManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/MeetingManage.java index 02cbe3f..bff0f4e 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/MeetingManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/manage/MeetingManage.java @@ -29,7 +29,7 @@ import com.hz.pm.api.meeting.entity.enumeration.MeetingStatusEnum; import com.hz.pm.api.meeting.entity.req.*; import com.hz.pm.api.meeting.entity.vo.*; import com.hz.pm.api.meeting.helper.ExpertInviteHelper; -import com.hz.pm.api.meeting.helper.MeetingCallOrMsgHelper; +import com.hz.pm.api.meeting.helper.MeetingNotifyHelper; import com.hz.pm.api.meeting.helper.MeetingManageHelper; import com.hz.pm.api.meeting.service.*; import com.hz.pm.api.meeting.task.ExpertRandomInviteTask; @@ -91,7 +91,7 @@ public class MeetingManage { private final IBelongOrgService belongOrgService; private final IDingOrganizationService dingOrganizationService; private final ExpertInviteHelper expertInviteHelper; - private final MeetingCallOrMsgHelper meetingCallOrMsgHelper; + private final MeetingNotifyHelper meetingNotifyHelper; private final IMeetingExpertJudgeService expertJudgeService; private final IMeetingExpertJudgeService meetingExpertJudgeService; @@ -686,7 +686,7 @@ public class MeetingManage { // 发送通知给专家 List experts = meetingExpertService.listAgreedExperts(meetingId); if (!experts.isEmpty()) { - meetingCallOrMsgHelper.sendCancelMeetingMsg(experts, meeting); + meetingNotifyHelper.sendCancelMeetingMsg(experts, meeting); } } finally { distributedLock.releaseLock(key); @@ -846,7 +846,7 @@ public class MeetingManage { .in(MeetingExpert::getId, currConfirmedMeIds) .set(MeetingExpert::getConfirmedRoster, Boolean.TRUE); meetingExpertService.update(meUpdate); - meetingCallOrMsgHelper.sendConfirmedRosterMsg(expertNoticing, meeting); + meetingNotifyHelper.sendConfirmedRosterMsg(expertNoticing, meeting); } finally { distributedLock.releaseLock(key); } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertCallResultRewriteTask.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertCallResultRewriteTask.java index 35afce0..cd9dbd1 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertCallResultRewriteTask.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertCallResultRewriteTask.java @@ -1,47 +1,33 @@ package com.hz.pm.api.meeting.task; -import cn.hutool.core.util.ObjectUtil; -import com.alibaba.fastjson.JSONObject; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.hz.pm.api.common.util.StrUtils; -import com.hz.pm.api.external.sms.SmsServiceClient; -import com.hz.pm.api.external.sms.dto.SmsDTO; -import com.hz.pm.api.external.sms.vo.SmsReply; -import com.hz.pm.api.external.sms.vo.SmsReplyResponse; -import com.hz.pm.api.external.sms.vo.SmsSendResponse; +import com.hz.pm.api.common.util.ThreadPoolUtil; import com.hz.pm.api.meeting.entity.domain.ExpertInviteRule; -import com.hz.pm.api.meeting.entity.domain.Meeting; import com.hz.pm.api.meeting.entity.domain.MeetingExpert; -import com.hz.pm.api.meeting.entity.domain.MeetingExpertSms; import com.hz.pm.api.meeting.entity.dto.RandomInviteRuleDTO; -import com.hz.pm.api.meeting.entity.dto.SmsReplyDetails; -import com.hz.pm.api.meeting.entity.dto.YxtCallBackDTO; import com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum; import com.hz.pm.api.meeting.entity.enumeration.ExpertInviteTypeEnum; -import com.hz.pm.api.meeting.mapper.MeetingExpertSmsMapper; import com.hz.pm.api.meeting.service.IExpertInviteRuleService; import com.hz.pm.api.meeting.service.IMeetingExpertService; -import com.hz.pm.api.sms.constant.VoiceSmsTemplateConst; -import com.ningdatech.basic.util.StrPool; -import com.ningdatech.yxt.entity.SysMsgRecordDetail; -import com.ningdatech.yxt.service.ISysMsgRecordDetailService; +import com.hz.pm.api.sys.model.entity.MsgCallRecord; +import com.hz.pm.api.sys.model.entity.MsgCallRecord.ReplyStatus; +import com.hz.pm.api.sys.service.IMsgCallRecordService; +import com.ningdatech.basic.util.CollUtils; 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.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; -import java.util.stream.Collectors; -import static com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum.*; +import static com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum.NOTICING; /** *

@@ -58,321 +44,114 @@ import static com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum.*; @AllArgsConstructor public class ExpertCallResultRewriteTask { - private final RandomInviteProperties randomInviteProperties; - - private final ThreadPoolTaskScheduler scheduler; + private final RandomInviteProperties properties; private final IMeetingExpertService meetingExpertService; private final IExpertInviteRuleService inviteRuleService; - private final ISysMsgRecordDetailService msgRecordDetailService; - private final SmsServiceClient smsServiceClient; - private final MeetingExpertSmsMapper meetingExpertSmsMapper; + private final IMsgCallRecordService msgCallRecordService; - private final static int MINUTES_CALL_RESULT_FEEDBACK = 15; + private static final int OVERTIME = 30; private static final String AGREE_KEY = "1"; + private static final String REFUSE_KEY = "2"; @PostConstruct public void initTask() { - if (!randomInviteProperties.getEnable()) { + if (Boolean.FALSE.equals(properties.getEnable())) { log.warn("随机邀请已关闭……"); return; } // 处理电话结果回填 - Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES); - Duration fixedRate = Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()); -// scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, fixedRate); - + Instant startTime = Instant.now().plus(properties.getResultRewriteFixedRate(), ChronoUnit.MINUTES); + Duration fixedRate = Duration.ofMinutes(properties.getResultRewriteFixedRate()); // 处理短信结果回填 - Instant smsStartTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES); - scheduler.scheduleAtFixedRate(this::expertSmsReply, smsStartTime, fixedRate); + ThreadPoolUtil.SCHEDULER.scheduleAtFixedRate(this::expertReplyRewrite, startTime, fixedRate); } - public void expertSmsReply() { - log.info("开始执行短信回复查询任务:{}", Thread.currentThread().getName()); + private void expertReplyRewrite() { + log.info("开始执行短信回复查询任务"); // 查询所有邀请的专家信息 状态为通话中的 - LambdaQueryWrapper meQuery = Wrappers.lambdaQuery(MeetingExpert.class) + Wrapper meQuery = Wrappers.lambdaQuery(MeetingExpert.class) .eq(MeetingExpert::getStatus, NOTICING.getCode()) + .isNotNull(MeetingExpert::getSubmitKey) .eq(MeetingExpert::getInviteType, ExpertInviteTypeEnum.RANDOM.getCode()); List experts = meetingExpertService.list(meQuery); if (experts.isEmpty()) { log.info("暂无短信回复任务执行"); return; } - // 所有随机邀请的短信Uuid - List smsUuids = experts.stream() - .map(expert -> expert.getSmsUuid()) - .filter(uuid -> uuid != null) - .distinct() - .collect(Collectors.toList()); - Set randomRuleIds = experts.stream() - .map(MeetingExpert::getRuleId).collect(Collectors.toSet()); + List submitKeys = CollUtils.fieldList(experts, MeetingExpert::getSubmitKey); + List mobiles = CollUtils.fieldList(experts, MeetingExpert::getMobile); + Set ruleIds = CollUtils.fieldSet(experts, MeetingExpert::getRuleId); // 查询随机邀请回调等待时间 - Map callbackMinutes = new HashMap<>(randomRuleIds.size()); - if (!randomRuleIds.isEmpty()) { - List inviteRules = inviteRuleService.listByIds(randomRuleIds); - inviteRules.forEach(expert -> { - RandomInviteRuleDTO rule = JSONObject.parseObject(expert.getInviteRule(), RandomInviteRuleDTO.class); - callbackMinutes.put(expert.getId(), rule.getWaitForCallbackMinutes()); - }); + Map callbackMinutes = new HashMap<>(ruleIds.size()); + List rules = inviteRuleService.listByIds(ruleIds); + for (ExpertInviteRule rule : rules) { + RandomInviteRuleDTO ruleDetail = JSONUtil.toBean(rule.getInviteRule(), RandomInviteRuleDTO.class); + callbackMinutes.put(rule.getId(), ruleDetail.getWaitForCallbackMinutes()); } - - // 获取专家回复内容 - SmsReplyDetails smsReplyDetails = viewSmsReplies(smsUuids); - - List updates = new ArrayList<>(); - + // 查询短信语音回执结果 + Wrapper mcrQuery = Wrappers.lambdaQuery(MsgCallRecord.class) + .in(MsgCallRecord::getSubmitKey, submitKeys) + .in(MsgCallRecord::getReceivePhone, mobiles) + .isNotNull(MsgCallRecord::getReplyOn); + List records = msgCallRecordService.list(mcrQuery); + Map replayMap = CollUtils.listToMap(records, + w -> w.getSubmitKey() + "#" + w.getReceivePhone()); + + List changes = new ArrayList<>(); for (MeetingExpert expert : experts) { - - Integer minutes = ObjectUtil.defaultIfNull(callbackMinutes.get(expert.getRuleId()), MINUTES_CALL_RESULT_FEEDBACK); - // 判断回复状态 - Optional status = getStatusByMsgRecordDetail(smsReplyDetails, minutes, expert); - if (status.isPresent()) { - MeetingExpert update = new MeetingExpert(); - update.setUpdateBy(0L); - update.setUpdateOn(LocalDateTime.now()); - update.setId(expert.getId()); - update.setStatus(status.get()); - updates.add(update); - } - } - meetingExpertService.updateBatchById(updates); - } - - - public void rewritePhoneCallResult() { - log.info("开始执行电话结果回填任务:{}", Thread.currentThread().getName()); - // 查询所有邀请的专家信息 状态为通话中的 - LambdaQueryWrapper meQuery = Wrappers.lambdaQuery(MeetingExpert.class) - .eq(MeetingExpert::getStatus, NOTICING.getCode()) - .eq(MeetingExpert::getInviteType, ExpertInviteTypeEnum.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 = JSONObject.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class); - callbackMinutes.put(w.getId(), rule.getWaitForCallbackMinutes()); + String submitKeyMobile = expert.getSubmitKey() + "#" + expert.getMobile(); + Integer overtime = callbackMinutes.getOrDefault(expert.getRuleId(), OVERTIME); + MsgCallRecord mcr = replayMap.get(submitKeyMobile); + buildStatus(mcr, overtime, expert).ifPresent(w -> { + expert.setStatus(w.getCode()); + changes.add(expert); }); } - 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 = ObjectUtil.defaultIfNull(callbackMinutes.get(expert.getRuleId()), MINUTES_CALL_RESULT_FEEDBACK); - 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.setStatus(status.get()); - if (AGREED.eq(update.getStatus())) { - // 发送专家确认参加的短信通知 - // sendAgreeNotice(expert); - agrees.add(expert); - } - updates.add(update); - } - } - meetingExpertService.updateBatchById(updates); + meetingExpertService.updateBatchById(changes); } - private static Optional getStatusByMsgRecordDetail(SysMsgRecordDetail mrd, int minutes, LocalDateTime createOn) { - LocalDateTime limitTime = LocalDateTime.now().minusMinutes(minutes); - String callBackJson = mrd.getCallBackJson(); - boolean waiting = limitTime.isBefore(createOn); - boolean hasCallBack = StrUtils.isNotBlank(callBackJson); - if (!hasCallBack && waiting) { - return Optional.empty(); - } + /** + * 构建专家状态 + * + * @param mcr 回复记录 + * @param overtime 超时时间 + * @param expert 专家 + * @return 专家状态 + */ + private static Optional buildStatus(MsgCallRecord mcr, + Integer overtime, + MeetingExpert expert) { + LocalDateTime now = LocalDateTime.now(); + boolean isOvertime = expert.getCreateOn().plusMinutes(overtime).isBefore(now); ExpertAttendStatusEnum status; - if (hasCallBack) { - try { - YxtCallBackDTO callback = JSONObject.parseObject(callBackJson, YxtCallBackDTO.class); - LocalDateTime dialBeginTime = callback.getDialBeginTime(); - if (dialBeginTime == null) { - return Optional.empty(); - } - Integer resultCode = callback.getResultCode(); - if (resultCode != null && resultCode == 0) { - String pressKey = callback.getPressKey(); - if (pressKey != null) { - pressKey = pressKey.replaceAll("\\*", "").trim(); - } - if (StrUtils.isBlank(pressKey) && waiting) { - return Optional.empty(); - } - status = AGREE_KEY.equals(pressKey) ? AGREED : REFUSED; - } else { - if (waiting) { - return Optional.empty(); - } - status = REFUSED; + if (mcr != null || isOvertime) { + if (isOvertime) { + status = ExpertAttendStatusEnum.UNANSWERED; + } else { + ReplyStatus replyStatus = mcr.getReplyStatus(); + switch (replyStatus) { + case OVERTIME: + case FAILED: + status = ExpertAttendStatusEnum.UNANSWERED; + break; + case SUCCESS: + String reply = StrUtil.trim(mcr.getReplyContent()); + if (AGREE_KEY.equals(reply)) { + status = ExpertAttendStatusEnum.AGREED; + } else { + status = ExpertAttendStatusEnum.REFUSED; + } + break; + default: + status = null; + break; } - } catch (Exception e) { - log.error("获取电话回调结果异常{}", mrd, e); - status = UNANSWERED; } } else { - // 超时未回复设置为拒绝参加 - status = REFUSED; + status = null; } - return Optional.of(status.getCode()); + return Optional.ofNullable(status); } - private static Optional getStatusByMsgRecordDetail(SmsReplyDetails smsReplyDetails, int minutes, MeetingExpert expert) { - Map> accept = smsReplyDetails.getAccept() //回复1同意参加的手机号 - , reject = smsReplyDetails.getReject() //回复2拒绝参加的手机号 - , other = smsReplyDetails.getOther(); //回复其他的专家 - Map> errorPhones = new HashMap<>();//发送失败的专家 - // 专家最长响应时间 - LocalDateTime limitTime = LocalDateTime.now().minusMinutes(minutes); - // 专家抽取时间 - boolean waiting = limitTime.isBefore(expert.getCreateOn()); - ExpertAttendStatusEnum status = NOTICING; - boolean hasCallBack = ObjectUtil.isEmpty(smsReplyDetails); - if (hasCallBack && waiting) { - return Optional.empty(); - } -// if (!waiting) { -// status = REFUSED; -// } - List errorPhoneList = errorPhones.get(expert.getSmsUuid()); - if(ObjectUtil.isNotEmpty(errorPhoneList) && errorPhoneList.contains(expert.getMobile())){ - // 未应答 - status = UNANSWERED; - } - if(ObjectUtil.isNotEmpty(accept)) { - List smsRepliesAccept = accept.get(expert.getSmsUuid()); - if (ObjectUtil.isNotEmpty(smsRepliesAccept)) { - boolean containsReplyMobile = smsRepliesAccept.stream() - .anyMatch(reply -> reply.getReplyMobile().equals(expert.getMobile())); - if (containsReplyMobile) { - SmsReply filteredReplies = smsRepliesAccept.stream() - .filter(reply -> reply.getReplyMobile().equals(expert.getMobile())) - .collect(Collectors.toList()).get(0); - // 回复时间 - Instant instant = Instant.ofEpochMilli(filteredReplies.getReplyReplytime()); - LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - if (localDateTime.isBefore(expert.getCreateOn().plusMinutes(minutes))) { - status = AGREED; - } else { - status = REFUSED; - } - } - } - } - if(ObjectUtil.isNotEmpty(reject)) { - List smsRepliesReject = reject.get(expert.getSmsUuid()); - if (ObjectUtil.isNotEmpty(smsRepliesReject)) { - boolean containsReplyMobile = smsRepliesReject.stream() - .anyMatch(reply -> reply.getReplyMobile().equals(expert.getMobile())); - if (containsReplyMobile) { - status = REFUSED; - } - } - } - if(ObjectUtil.isNotEmpty(other)) { - List smsRepliesOther = other.get(expert.getSmsUuid()); - if (ObjectUtil.isNotEmpty(smsRepliesOther)) { - boolean containsReplyMobile = smsRepliesOther.stream() - .anyMatch(reply -> reply.getReplyMobile().equals(expert.getMobile())); - if (containsReplyMobile) { - status = REFUSED; - } - } - } - return Optional.of(status.getCode()); - } - - // 发送短信提醒 - public String sendExpertSms(List expertMeetings, Meeting meeting) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - // 短信内容 - String replacedContent = String.format(VoiceSmsTemplateConst.EXPERT_INVITE, - meeting.getCreator(), meeting.getName(), meeting.getStartTime().format(formatter) + "至" + meeting.getEndTime().format(formatter), meeting.getMeetingAddress()); - - List phones = expertMeetings.stream().map(obj -> obj.getMobile()).collect(Collectors.toList()); - SmsDTO smsSendResponseSmsDto = smsServiceClient.smsSend(replacedContent, phones); - // 短信发送成功返回的UUID - String resultUuid = smsSendResponseSmsDto.getData().getResult(); - // 保存短信发送记录 - MeetingExpertSms meetingExpertSms = MeetingExpertSms.builder() - .meetingId(meeting.getId().toString()) - .createOn(LocalDateTime.now()) - .smsUuid(resultUuid) - .content(replacedContent) - .phones(phones.stream() - .collect(Collectors.joining(", "))) - .smsResult(smsSendResponseSmsDto.toString()) - .build(); - meetingExpertSmsMapper.insert(meetingExpertSms); - return resultUuid; - } - - // 查看短信回复内容并更改专家回复状态 - public SmsReplyDetails viewSmsReplies(List uuids) { - Map> accept = new HashMap<>() - , reject = new HashMap<>() - , other = new HashMap<>(); - Map> errorPhones = new HashMap<>(); - uuids.forEach(uuid -> { - SmsReplyResponse response = smsServiceClient.smsReply(uuid); - List smsReplies = response.getResult(); - // 成功回复的手机信息 - if (ObjectUtil.isNotEmpty(smsReplies)){ - // 只有第一条回复的内容有效 - smsReplies = smsReplies.stream() - .collect(Collectors.groupingBy(SmsReply::getReplyMobile, - Collectors.minBy(Comparator.comparing(SmsReply::getReplyReplytime)))) - .values().stream() - .map(Optional::get) - .collect(Collectors.toList()); - accept.put(uuid, smsReplies.stream() - .filter(obj -> ObjectUtil.isNotEmpty(obj)) - .filter(smsReply -> "1".equals(smsReply.getReplyContent())) - .collect(Collectors.toList())); - reject.put(uuid, smsReplies.stream() - .filter(obj -> ObjectUtil.isNotEmpty(obj)) - .filter(smsReply -> "2".equals(smsReply.getReplyContent())) - .collect(Collectors.toList())); - other.put(uuid, smsReplies.stream() - .filter(obj -> ObjectUtil.isNotEmpty(obj)) - .filter(smsReply -> !("1".equals(smsReply.getReplyContent()) || "2".equals(smsReply.getReplyContent()))) - .collect(Collectors.toList())); - } - // 发送失败的手机号 - if (StrUtils.isNotBlank(response.getErrorPhone())){ - errorPhones.put(uuid, Arrays.asList(response.getErrorPhone().split(","))); - } - }); - - SmsReplyDetails smsReplyDetails = SmsReplyDetails.builder() - .accept(accept) - .reject(reject) - .errorPhones(errorPhones).build(); - - return smsReplyDetails; - } } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertRandomInviteTask.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertRandomInviteTask.java index f7a3293..f674ea0 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertRandomInviteTask.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/ExpertRandomInviteTask.java @@ -14,7 +14,7 @@ import com.hz.pm.api.meeting.entity.dto.InviteCacheDTO; import com.hz.pm.api.meeting.entity.dto.RandomInviteRuleDTO; import com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum; import com.hz.pm.api.meeting.entity.req.ConfirmedRosterReq; -import com.hz.pm.api.meeting.helper.MeetingCallOrMsgHelper; +import com.hz.pm.api.meeting.helper.MeetingNotifyHelper; import com.hz.pm.api.meeting.manage.ExpertInviteManage; import com.hz.pm.api.meeting.manage.MeetingManage; import com.hz.pm.api.meeting.service.IExpertInviteAvoidRuleService; @@ -72,7 +72,7 @@ public class ExpertRandomInviteTask { private final IMeetingService meetingService; private final ExpertInviteManage expertInviteManage; private final IExpertInviteAvoidRuleService inviteAvoidRuleService; - private final MeetingCallOrMsgHelper meetingCallOrMsgHelper; + private final MeetingNotifyHelper meetingNotifyHelper; /** * 用来存入线程执行句柄, 停止定时任务时使用 @@ -227,7 +227,7 @@ public class ExpertRandomInviteTask { public void invite(Long meetingId, Boolean reInvite, LocalDateTime tsTime) { log.info("开始进行专家后台抽取:{}", meetingId); if (!inInviteTimeRange()) { - log.warn("不在会议抽取执行时间:{}",meetingId); + log.warn("不在会议抽取执行时间:{}", meetingId); return; } Meeting meeting = meetingService.getById(meetingId); @@ -268,9 +268,8 @@ public class ExpertRandomInviteTask { expert.setStatus(ExpertAttendStatusEnum.NOTICING.getCode()); return expert; }); -// meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertMeetings); // 短信或电话提醒 - meetingCallOrMsgHelper.smsOrCallExpertByMeeting(meeting, expertMeetings); + meetingNotifyHelper.notifyExperts(meeting, expertMeetings); log.info("会议:{} 后台抽取专家:{}名", meetingId, expertMeetings.size()); meetingExpertService.saveBatch(expertMeetings); } else { @@ -283,7 +282,7 @@ public class ExpertRandomInviteTask { meetingService.stopRandomInvite(meetingId); if (notIgnoreCnt.get() == notSupportCnt.get() && notIgnoreCnt.get() > 0) { // 当未完成抽取且无专家可抽取时 - meetingCallOrMsgHelper.sendInviteStopMsg(meeting.getCreateBy(), meetingId, meeting.getName()); + meetingNotifyHelper.sendInviteStopMsg(meeting.getCreateBy(), meetingId, meeting.getName()); } } // 所有抽取规则抽取人数满足 自动召开会议 diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/MsgCallReplyRewriteTask.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/MsgCallReplyRewriteTask.java new file mode 100644 index 0000000..d816b1c --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/task/MsgCallReplyRewriteTask.java @@ -0,0 +1,171 @@ +package com.hz.pm.api.meeting.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hz.pm.api.common.helper.MsgCallHelper; +import com.hz.pm.api.common.util.BizUtils; +import com.hz.pm.api.common.util.MDCThreadPoolTaskExecutor; +import com.hz.pm.api.external.sms.vo.SmsReply; +import com.hz.pm.api.external.sms.vo.SmsReplyDTO; +import com.hz.pm.api.sys.model.entity.MsgCallRecord; +import com.hz.pm.api.sys.model.entity.MsgCallRecord.ReplyStatus; +import com.hz.pm.api.sys.model.entity.MsgCallRecord.SendStatus; +import com.hz.pm.api.sys.model.enumeration.SubmitTypeEnum; +import com.hz.pm.api.sys.service.IMsgCallRecordService; +import com.ningdatech.basic.util.CollUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static com.hz.pm.api.external.MobileCallClient.ReplyCallDTO; + +/** + *

+ * MsgCallReplyRewriteTask + *

+ * + * @author WendyYang + * @since 21:29 2024/4/25 + */ +@Slf4j +@Component +public class MsgCallReplyRewriteTask { + + private static final ThreadPoolTaskExecutor EXECUTOR; + + static { + // 初始化线程池 + EXECUTOR = new MDCThreadPoolTaskExecutor(); + EXECUTOR.setCorePoolSize(2); + EXECUTOR.setMaxPoolSize(4); + EXECUTOR.setQueueCapacity(100); + EXECUTOR.setKeepAliveSeconds(120); + EXECUTOR.setThreadPriority(Thread.NORM_PRIORITY); + EXECUTOR.setWaitForTasksToCompleteOnShutdown(true); + EXECUTOR.setThreadNamePrefix("callRewriteExecutor-"); + EXECUTOR.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + } + + private final IMsgCallRecordService msgCallRecordService; + private final MsgCallHelper msgCallHelper; + + public MsgCallReplyRewriteTask(IMsgCallRecordService msgCallRecordService, MsgCallHelper msgCallHelper) { + this.msgCallRecordService = msgCallRecordService; + this.msgCallHelper = msgCallHelper; + EXECUTOR.initialize(); + } + + + @Scheduled(fixedRateString = "${msg-call-reply-write.fixed-rate:2}", timeUnit = TimeUnit.MINUTES) + public void replyRewrite() { + log.info("开始获取短信电话回执"); + Wrapper query = Wrappers.lambdaQuery(MsgCallRecord.class) + .eq(MsgCallRecord::getNeedReply, Boolean.TRUE) + .eq(MsgCallRecord::getSendStatus, SendStatus.SUCCESS) + .eq(MsgCallRecord::getReplyStatus, ReplyStatus.WAITING) + .isNull(MsgCallRecord::getReplyOn); + List records = msgCallRecordService.list(query); + if (records.isEmpty()) { + return; + } + LocalDateTime startTime = LocalDateTime.now().minusMinutes(60); + Map> map = CollUtils.group(records, w -> w.getCreateOn().isBefore(startTime)); + List overtimeRecords = map.get(Boolean.TRUE); + if (CollUtil.isNotEmpty(overtimeRecords)) { + overtimeRecords.forEach(w -> { + w.setReplyStatus(ReplyStatus.OVERTIME); + w.setReplyOn(LocalDateTime.now()); + }); + msgCallRecordService.updateBatchById(overtimeRecords); + } + List waitingRecords = map.get(Boolean.FALSE); + if (CollUtil.isNotEmpty(waitingRecords)) { + CollUtils.group(waitingRecords, MsgCallRecord::getSubmitType) + .forEach((submitType, recordsBySummitType) -> { + if (SubmitTypeEnum.CALL.equals(submitType)) { + rewriteCallReply(recordsBySummitType); + } else if (SubmitTypeEnum.SMS.equals(submitType)) { + rewriteSmsReply(recordsBySummitType); + } + }); + } + } + + private void rewriteSmsReply(List records) { + CollUtils.group(records, MsgCallRecord::getSubmitKey) + .forEach((submitKey, recordsBySubmitKey) -> { + Runnable function = () -> { + try { + SmsReplyDTO retReply = msgCallHelper.smsReply(submitKey); + String errorPhoneStr = retReply.getErrorPhone(); + List errorPhones = StrUtil.split(errorPhoneStr, ","); + List replies = retReply.getResult(); + Map currReplyMap = new HashMap<>(); + if (replies != null && !replies.isEmpty()) { + replies.sort(Comparator.comparing(SmsReply::getReplyReplytime)); + replies.forEach(w -> currReplyMap.putIfAbsent(w.getReplyMobile(), w)); + } + List updates = new ArrayList<>(); + for (MsgCallRecord w : recordsBySubmitKey) { + if (errorPhones.contains(w.getReceivePhone())) { + w.setReplyStatus(ReplyStatus.FAILED); + w.setReplyOn(LocalDateTime.now()); + } else if (currReplyMap.containsKey(w.getReceivePhone())) { + SmsReply reply = currReplyMap.get(w.getReceivePhone()); + w.setReplyOn(LocalDateTime.from(Instant.ofEpochMilli(reply.getReplyReplytime()))); + w.setReplyContent(reply.getReplyContent()); + w.setReplyStatus(ReplyStatus.SUCCESS); + } + if (w.getReplyOn() != null) { + updates.add(w); + } + } + msgCallRecordService.updateBatchById(updates); + } catch (Exception e) { + log.error("回复结果回填失败:{}", submitKey, e); + } + }; + EXECUTOR.execute(function); + }); + } + + private void rewriteCallReply(List records) { + CollUtils.group(records, MsgCallRecord::getSubmitKey) + .forEach((submitKey, recordsBySubmitKey) -> { + Runnable function = () -> { + try { + List retReplies = msgCallHelper.callReply(submitKey); + Map replyMap = BizUtils.groupFirstMap(retReplies, + ReplyCallDTO::getRemoteNumber, + Comparator.comparing(ReplyCallDTO::getSendDate)); + List updates = new ArrayList<>(); + for (MsgCallRecord w : recordsBySubmitKey) { + ReplyCallDTO reply = replyMap.get(w.getReceivePhone()); + if (reply != null) { + w.setReplyOn(LocalDateTimeUtil.of(reply.getSendDate())); + w.setReplyContent(reply.getDtmf()); + w.setReplyStatus(ReplyStatus.SUCCESS); + updates.add(w); + } + } + msgCallRecordService.updateBatchById(updates); + } catch (Exception e) { + log.error("回复结果回填失败:{}", submitKey, e); + } + }; + EXECUTOR.execute(function); + }); + } + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/manage/ConstructionManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/manage/ConstructionManage.java index 6a61c71..199289d 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/manage/ConstructionManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/manage/ConstructionManage.java @@ -599,33 +599,35 @@ public class ConstructionManage { purchase.setXcfhxApplyFiles(req.getXcfhxApplyFiles()); purchase.setMatchXcfhx(req.getMatchXcfhx()); purchase.setXcfhxApplyRemark(req.getXcfhxApplyRemark()); - if (StrUtils.isBlank(purchase.getMhXcfhxReportFile()) || - req.getMhXcfhxReportFile().equals(purchase.getMhXcfhxReportFile())) { - ApiResponse retFileInfo; - File tmpFile = null; - FileInputStream fis = null; - try { - tmpFile = mhFileClient.downloadToTmpFile(req.getMhXcfhxReportFile()); - String fileName = purchase.getBidName() + "-信创符合性测评报告." + FileUtil.getSuffix(tmpFile); - fis = new FileInputStream(tmpFile); - String mimeType = FileUtil.getMimeType(tmpFile.getPath()); - MockMultipartFile multipartFile = new MockMultipartFile(fileName, fileName, mimeType, fis); - retFileInfo = fileController.upload(multipartFile, "default"); - } catch (IOException e) { - log.error("信创报告上传失败", e); - throw BizException.wrap("信创符合性测评报告上传失败"); - } finally { - if (tmpFile != null) { - tmpFile.deleteOnExit(); - } - if (fis != null) { - IOUtils.closeQuietly(fis); + if (Boolean.TRUE.equals(req.getMatchXcfhx())) { + if (StrUtils.isBlank(purchase.getMhXcfhxReportFile()) || + req.getMhXcfhxReportFile().equals(purchase.getMhXcfhxReportFile())) { + ApiResponse retFileInfo; + File tmpFile = null; + FileInputStream fis = null; + try { + tmpFile = mhFileClient.downloadToTmpFile(req.getMhXcfhxReportFile()); + String fileName = purchase.getBidName() + "-信创符合性测评报告." + FileUtil.getSuffix(tmpFile); + fis = new FileInputStream(tmpFile); + String mimeType = FileUtil.getMimeType(tmpFile.getPath()); + MockMultipartFile multipartFile = new MockMultipartFile(fileName, fileName, mimeType, fis); + retFileInfo = fileController.upload(multipartFile, "default"); + } catch (IOException e) { + log.error("信创报告上传失败", e); + throw BizException.wrap("信创符合性测评报告上传失败"); + } finally { + if (tmpFile != null) { + tmpFile.deleteOnExit(); + } + if (fis != null) { + IOUtils.closeQuietly(fis); + } } + purchase.setXcfhxReportFiles(JSONUtil.toJsonStr(retFileInfo)); } - purchase.setXcfhxReportFiles(JSONUtil.toJsonStr(retFileInfo)); + purchase.setMhXcfhxReportRecordId(req.getMhXcfhxReportRecordId()); + purchase.setMhXcfhxReportFile(req.getMhXcfhxReportFile()); } - purchase.setMhXcfhxReportRecordId(req.getMhXcfhxReportRecordId()); - purchase.setMhXcfhxReportFile(req.getMhXcfhxReportFile()); xcfhxStateMachineUtil.pass(purchase); purchaseService.updateById(purchase); diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/model/req/XcfhxApplyReq.java b/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/model/req/XcfhxApplyReq.java index 7daf9ba..6ff3452 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/model/req/XcfhxApplyReq.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/projectdeclared/model/req/XcfhxApplyReq.java @@ -26,11 +26,9 @@ public class XcfhxApplyReq { private Long bidId; @ApiModelProperty("信创符合性测评报告文件") - @NotBlank(message = "信创符合性测评报告文件不能为空") private String mhXcfhxReportFile; @ApiModelProperty("信创符合性测评记录ID") - @NotBlank(message = "信创符合性测评记录ID不能为空") private String mhXcfhxReportRecordId; @ApiModelProperty("是否符合信创符合性要求") diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sms/manage/VerificationCodeManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/sms/manage/VerificationCodeManage.java index 938c78e..a2f3453 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/sms/manage/VerificationCodeManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sms/manage/VerificationCodeManage.java @@ -2,18 +2,18 @@ package com.hz.pm.api.sms.manage; import cn.hutool.core.util.PhoneUtil; import cn.hutool.core.util.RandomUtil; +import com.hz.pm.api.common.helper.MsgCallHelper; +import com.hz.pm.api.external.sms.MhSmsClient; import com.hz.pm.api.sms.constant.VerificationCodeType; import com.hz.pm.api.sms.constant.VoiceSmsTemplateConst; import com.hz.pm.api.sms.model.dto.VerifyCodeCacheDTO; import com.hz.pm.api.sms.model.po.ReqVerificationCodePO; import com.hz.pm.api.sms.utils.DateUtil; import com.hz.pm.api.sms.utils.SmsRedisKeyUtils; +import com.hz.pm.api.sys.model.enumeration.BizTypeEnum; import com.ningdatech.basic.exception.BizException; import com.ningdatech.cache.model.cache.CacheKey; import com.ningdatech.cache.repository.CachePlusOps; -import com.ningdatech.yxt.client.YxtClient; -import com.ningdatech.yxt.model.cmd.SendSmsCmd; -import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -34,7 +34,7 @@ import java.util.Objects; @RequiredArgsConstructor public class VerificationCodeManage { - private final YxtClient yxtClient; + private final MsgCallHelper msgCallHelper; private final CachePlusOps cachePlusOps; public void sendVerificationCode(ReqVerificationCodePO req) { @@ -62,22 +62,15 @@ public class VerificationCodeManage { .build(); // 创建短信内容 - SendSmsCmd sendSmsCmd = new SendSmsCmd(); switch (codeType) { case LOGIN: { - SendSmsContext sendSmsCtx = new SendSmsContext(); - sendSmsCtx.setReceiveNumber(req.getMobile()); - sendSmsCtx.setContent(String.format(VoiceSmsTemplateConst.SMS_VERIFY_CODE, code, codeType.getExpireTime())); - sendSmsCmd.setContextList(Collections.singletonList(sendSmsCtx)); -// sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); + String content = String.format(VoiceSmsTemplateConst.SMS_VERIFY_CODE, code, codeType.getExpireTime()); + msgCallHelper.sendMsg(content, req.getMobile(), BizTypeEnum.VERIFY_CODE); } break; case EXPERT_REGISTER: { - SendSmsContext sendSmsCtx = new SendSmsContext(); - sendSmsCtx.setReceiveNumber(req.getMobile()); - sendSmsCtx.setContent(String.format(VoiceSmsTemplateConst.EXPERT_REGISTER, code, codeType.getExpireTime())); - sendSmsCmd.setContextList(Collections.singletonList(sendSmsCtx)); -// sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); + String content = String.format(VoiceSmsTemplateConst.EXPERT_REGISTER, code, codeType.getExpireTime()); + msgCallHelper.sendMsg(content, req.getMobile(), BizTypeEnum.VERIFY_CODE); } break; default: @@ -85,7 +78,6 @@ public class VerificationCodeManage { } // 发送 短信 - yxtClient.submitSmsTask(sendSmsCmd); log.info("发送短信验证码:{} -> {}", req.getMobile(), code); cachePlusOps.set(new CacheKey(cacheKey, Duration.ofMinutes(codeType.getExpireTime())), cache); diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sms/task/YxtPollingTask.java b/hz-pm-api/src/main/java/com/hz/pm/api/sms/task/YxtPollingTask.java deleted file mode 100644 index e8eea91..0000000 --- a/hz-pm-api/src/main/java/com/hz/pm/api/sms/task/YxtPollingTask.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.hz.pm.api.sms.task; - -import com.ningdatech.yxt.client.YxtContext; -import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -/** - * @author liuxinxin - * @date 2022/8/9 下午3:58 - * 音信通定时检查电话结果 - */ -@Component -@RequiredArgsConstructor -public class YxtPollingTask { - - - private final YxtContext yxtContext; - - /** - * 校验音信通结果数据 - * 每5分钟执行一次 - */ - @Scheduled(cron = "0 */1 * * * ?") - public void smsMsgResultCheck() { - yxtContext.smsMsgResultCheck(); - } -} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/controller/NdMsgCallRecordController.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/controller/NdMsgCallRecordController.java new file mode 100644 index 0000000..f96e425 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/controller/NdMsgCallRecordController.java @@ -0,0 +1,18 @@ +package com.hz.pm.api.sys.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.stereotype.Controller; + +/** + *

+ * 前端控制器 + *

+ * + * @author WendyYang + * @since 2024-04-25 + */ +@Controller +@RequestMapping("/api.sys/ndMsgCallRecord") +public class NdMsgCallRecordController { + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/manage/EarlyWarningManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/manage/EarlyWarningManage.java index 8b1c9bb..074c5e1 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/sys/manage/EarlyWarningManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/manage/EarlyWarningManage.java @@ -1,8 +1,6 @@ package com.hz.pm.api.sys.manage; -import com.hz.pm.api.common.enumeration.CommonEnum; import com.hz.pm.api.common.helper.UserInfoHelper; -import com.hz.pm.api.meeting.helper.YxtClientHelper; import com.hz.pm.api.projectlib.model.entity.Project; import com.hz.pm.api.projectlib.model.enumeration.InstTypeEnum; import com.hz.pm.api.projectlib.model.enumeration.WarningFlowTypeEnum; @@ -13,7 +11,6 @@ import com.hz.pm.api.sys.model.entity.WflowEarlyWarningRecords; import com.hz.pm.api.sys.service.IEarlyWarningRecordsService; import com.hz.pm.api.sys.service.INotifyService; import com.hz.pm.api.user.security.model.UserFullInfoDTO; -import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; import com.wflow.enums.WarningNoticeTypeEnum; import com.wflow.enums.WarningRuleTypeEnum; import lombok.AllArgsConstructor; @@ -37,13 +34,8 @@ import java.util.Objects; public class EarlyWarningManage { private final UserInfoHelper userInfoHelper; - private final NoticeManage noticeManage; - private final IEarlyWarningRecordsService earlyWarningRecordsService; - - private final YxtClientHelper yxtClientHelper; - private final INotifyService notifyService; /** @@ -147,14 +139,6 @@ public class EarlyWarningManage { Notify notify = noticeManage.assemblyAuditNotify(user.getUserId(), project, content); notify.setType(MsgTypeEnum.PROJECT_REVIEW.name()); notifyService.save(notify); - - //3.发短信 - if (noticeMethod.contains(String.valueOf(CommonEnum.MOBILE.getCode()))) { - SendSmsContext context = new SendSmsContext(); - context.setReceiveNumber(user.getMobile()); - context.setContent(content); - yxtClientHelper.sendSms(context); - } } /** @@ -263,13 +247,6 @@ public class EarlyWarningManage { notify.setType(MsgTypeEnum.PROJECT_REVIEW.name()); notifyService.save(notify); - //3.发短信 - if (noticeMethod.contains(String.valueOf(CommonEnum.MOBILE.getCode()))) { - SendSmsContext context = new SendSmsContext(); - context.setReceiveNumber(user.getMobile()); - context.setContent(content); - yxtClientHelper.sendSms(context); - } } /** diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/mapper/MsgCallRecordMapper.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/mapper/MsgCallRecordMapper.java new file mode 100644 index 0000000..75100a8 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/mapper/MsgCallRecordMapper.java @@ -0,0 +1,16 @@ +package com.hz.pm.api.sys.mapper; + +import com.hz.pm.api.sys.model.entity.MsgCallRecord; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author WendyYang + * @since 2024-04-25 + */ +public interface MsgCallRecordMapper extends BaseMapper { + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/mapper/MsgCallRecordMapper.xml b/hz-pm-api/src/main/java/com/hz/pm/api/sys/mapper/MsgCallRecordMapper.xml new file mode 100644 index 0000000..a770064 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/mapper/MsgCallRecordMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/entity/MsgCallRecord.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/entity/MsgCallRecord.java new file mode 100644 index 0000000..8636fd1 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/entity/MsgCallRecord.java @@ -0,0 +1,124 @@ +package com.hz.pm.api.sys.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.hz.pm.api.sys.model.enumeration.BizTypeEnum; +import com.hz.pm.api.sys.model.enumeration.SubmitTypeEnum; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 短信语音记录表 + *

+ * + * @author WendyYang + * @since 2024-04-25 + */ +@Data +@TableName("ND_MSG_CALL_RECORD") +@ApiModel(value = "NdMsgCallRecord对象") +public class MsgCallRecord implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "ID", type = IdType.AUTO) + private Long id; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createOn; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateOn; + + @TableField(fill = FieldFill.INSERT) + private Long createBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updateBy; + + /** + * 业务类型 + */ + private BizTypeEnum bizType; + + /** + * 提交标识 + */ + private String submitKey; + + /** + * 提交类型 + */ + private SubmitTypeEnum submitType; + + /** + * 接收手机号 + */ + private String receivePhone; + + /** + * 短信内容 + */ + private String content; + + /** + * 模板编码 + */ + private String templateCode; + + /** + * 是否需要回复 + */ + private Boolean needReply; + + /** + * 发送状态: + */ + private SendStatus sendStatus; + + /** + * 回复状态 + */ + private ReplyStatus replyStatus; + + /** + * 回复内容 + */ + private String replyContent; + + /** + * 回复时间 + */ + private LocalDateTime replyOn; + + + /** + * 发送状态 + */ + public enum SendStatus { + + SUCCESS, + + FAILED + + } + + /** + * 发送状态 + */ + public enum ReplyStatus { + + SUCCESS, + + WAITING, + + OVERTIME, + + FAILED + + } + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/enumeration/BizTypeEnum.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/enumeration/BizTypeEnum.java new file mode 100644 index 0000000..5dcc120 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/enumeration/BizTypeEnum.java @@ -0,0 +1,32 @@ +package com.hz.pm.api.sys.model.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * BizTypeEnum + *

+ * + * @author WendyYang + * @since 16:13 2024/4/25 + */ +@Getter +@AllArgsConstructor +public enum BizTypeEnum { + + VERIFY_CODE("验证码"), + + MEETING_ROSTER_CONFIRM("会议名单确认"), + + EXPERT_LEAVE("专家请假"), + + MEETING_CANCEL("会议取消"), + + EXPERT_INVITE_STOP("会议取消"), + + EXPERT_INVITE("专家邀请"); + + private final String desc; + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/enumeration/SubmitTypeEnum.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/enumeration/SubmitTypeEnum.java new file mode 100644 index 0000000..5268ad2 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/model/enumeration/SubmitTypeEnum.java @@ -0,0 +1,17 @@ +package com.hz.pm.api.sys.model.enumeration; + +/** + *

+ * MsgTypeEnum + *

+ * + * @author WendyYang + * @since 15:26 2024/4/25 + */ +public enum SubmitTypeEnum { + + SMS, + + CALL + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/service/IMsgCallRecordService.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/service/IMsgCallRecordService.java new file mode 100644 index 0000000..64382c0 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/service/IMsgCallRecordService.java @@ -0,0 +1,16 @@ +package com.hz.pm.api.sys.service; + +import com.hz.pm.api.sys.model.entity.MsgCallRecord; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 服务类 + *

+ * + * @author WendyYang + * @since 2024-04-25 + */ +public interface IMsgCallRecordService extends IService { + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/sys/service/impl/MsgCallRecordServiceImpl.java b/hz-pm-api/src/main/java/com/hz/pm/api/sys/service/impl/MsgCallRecordServiceImpl.java new file mode 100644 index 0000000..f206e2f --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/sys/service/impl/MsgCallRecordServiceImpl.java @@ -0,0 +1,20 @@ +package com.hz.pm.api.sys.service.impl; + +import com.hz.pm.api.sys.model.entity.MsgCallRecord; +import com.hz.pm.api.sys.mapper.MsgCallRecordMapper; +import com.hz.pm.api.sys.service.IMsgCallRecordService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author WendyYang + * @since 2024-04-25 + */ +@Service +public class MsgCallRecordServiceImpl extends ServiceImpl implements IMsgCallRecordService { + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/workbench/manage/WorkbenchManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/workbench/manage/WorkbenchManage.java index 0c50072..031c587 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/workbench/manage/WorkbenchManage.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/workbench/manage/WorkbenchManage.java @@ -319,6 +319,12 @@ public class WorkbenchManage { .like(StrUtil.isNotBlank(req.getBuildOrg()), Project::getBuildOrgName, req.getBuildOrg()) .like(StrUtil.isNotBlank(req.getProjectName()), Project::getProjectName, req.getProjectName()) .in(Project::getBuildOrgCode, CollUtils.convert(viewUnitIds, String::valueOf)) + // 评审金额 + .ge(req.getReviewAmountMin() != null, Project::getReviewAmount, req.getReviewAmountMin()) + .le(req.getReviewAmountMax() != null, Project::getReviewAmount, req.getReviewAmountMax()) + // 下达金额 + .ge(req.getApproveAmountMin() != null, Project::getApprovalAmount, req.getApproveAmountMin()) + .ge(req.getApproveAmountMax() != null, Project::getApprovalAmount, req.getApproveAmountMax()) .eq(Project::getNewest, Boolean.TRUE) .orderByDesc(Project::getUpdateOn); switch (req.getProcessNode()) { @@ -367,6 +373,8 @@ public class WorkbenchManage { ProjectLibListItemVO item = new ProjectLibListItemVO(); item.setId(w.getId()); item.setProjectName(w.getProjectName()); + // 项目申报 + item.setFromType("1"); item.setProjectCode(w.getProjectCode()); item.setArea(w.getArea()); item.setAreaCode(w.getAreaCode()); @@ -382,6 +390,7 @@ public class WorkbenchManage { item.setInstCode(w.getInstCode()); item.setIsHigherSuperOrg(w.getIsHigherSuperOrg()); item.setApprovedAmount(w.getApprovalAmount()); + item.setReviewAmount(w.getReviewAmount()); item.setPrePlanProjectId(w.getPrePlanProjectId()); return item; }); diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/workbench/model/WorkbenchProjectLibReq.java b/hz-pm-api/src/main/java/com/hz/pm/api/workbench/model/WorkbenchProjectLibReq.java index f64175f..198b827 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/workbench/model/WorkbenchProjectLibReq.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/workbench/model/WorkbenchProjectLibReq.java @@ -5,6 +5,8 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; +import java.math.BigDecimal; + import static com.hz.pm.api.projectlib.handle.AbstractProcessHandle.ProcessNode; /** @@ -34,4 +36,16 @@ public class WorkbenchProjectLibReq extends PagePo { @ApiModelProperty("申报单位名称") private String buildOrg; + @ApiModelProperty("评审金额最小值") + private BigDecimal reviewAmountMin; + + @ApiModelProperty("评审金额最大值") + private BigDecimal reviewAmountMax; + + @ApiModelProperty("批复金额") + private BigDecimal approveAmountMin; + + @ApiModelProperty("批复金额") + private BigDecimal approveAmountMax; + } diff --git a/hz-pm-api/src/main/resources/application-dev.yml b/hz-pm-api/src/main/resources/application-dev.yml index 86fc475..f8e0e3f 100644 --- a/hz-pm-api/src/main/resources/application-dev.yml +++ b/hz-pm-api/src/main/resources/application-dev.yml @@ -154,7 +154,7 @@ sa-token: is-log: false yxt: -# wsdl-url: http://115.239.137.23:9501/ws/v1?wsdl + # wsdl-url: http://115.239.137.23:9501/ws/v1?wsdl wsdl-url: classpath:/wsdl.xml #账号 user-code: hzndkj @@ -194,9 +194,8 @@ auth-code: agent-login: proxy: secret-key: nqkwiqojg7g4eiypr3rb8s7nb4noa8b2 -sms: - client-url: http://10.54.38.13:8081/mh-gateway/auth-single - +sms-send: + host: http://10.54.38.13:8081/mh-gateway/auth-single # 提醒任务 reminder-task: diff --git a/hz-pm-api/src/main/resources/application-prod.yml b/hz-pm-api/src/main/resources/application-prod.yml index db0a928..df1f330 100644 --- a/hz-pm-api/src/main/resources/application-prod.yml +++ b/hz-pm-api/src/main/resources/application-prod.yml @@ -194,6 +194,10 @@ mh: upload-url: http://10.54.38.13:8081/mh-gateway/oss/oss/uploadFileSkipLogin purchase-notice: open: false + +sms-send: + host: http://10.54.38.13:8081/mh-gateway/auth-single + sync-mh-company: open: false sync-mh-user: diff --git a/hz-pm-api/src/test/java/com/hz/pm/api/external/MobileCallClientTest.java b/hz-pm-api/src/test/java/com/hz/pm/api/external/MobileCallClientTest.java new file mode 100644 index 0000000..3cad7bf --- /dev/null +++ b/hz-pm-api/src/test/java/com/hz/pm/api/external/MobileCallClientTest.java @@ -0,0 +1,42 @@ +package com.hz.pm.api.external; + +import com.hz.pm.api.AppTests; +import com.hz.pm.api.common.helper.MsgCallHelper; +import com.hz.pm.api.common.util.EnvironmentUtil; +import com.hz.pm.api.external.model.dto.CallRetDTO; +import com.hz.pm.api.sys.model.enumeration.BizTypeEnum; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import java.util.Collection; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +/** + *

+ * MobileCallClientTest + *

+ * + * @author WendyYang + * @since 16:34 2024/4/26 + */ +class MobileCallClientTest extends AppTests { + + @Autowired + private MsgCallHelper msgCallHelper; + + @Test + void sendCall() { + String format = String.format("【杭州数字信创】尊敬的专家您好,%s现邀请您作为专家参加%s会议,会议时间:%s,会议地点:%s。 确认参加请按 1,拒绝参加请按 2。请您选择", + "信创办", "测试语音", "2024年1月1日 12:30 至 2024年1月1日 12:30", "杭州市"); + String ret = msgCallHelper.sendCall("19530651430", format, BizTypeEnum.EXPERT_INVITE); + System.out.println(ret); + } + + @Test + void replyCall() { + System.out.println(msgCallHelper.callReply("3086259928697016320")); + } +} \ No newline at end of file diff --git a/hz-pm-api/src/test/resources/application-dev.yml b/hz-pm-api/src/test/resources/application-dev.yml index 66382c6..64b21b5 100644 --- a/hz-pm-api/src/test/resources/application-dev.yml +++ b/hz-pm-api/src/test/resources/application-dev.yml @@ -193,5 +193,7 @@ auth-code: agent-login: proxy: secret-key: nqkwiqojg7g4eiypr3rb8s7nb4noa8b2 -sms: - client-url: http://10.54.38.13:8081/mh-gateway/auth-single \ No newline at end of file +sms-send: + host: http://10.54.38.13:8081/mh-gateway/auth-single +mobile-call: + host: http://36.20.16.36:18181/blue_esl_api \ No newline at end of file diff --git a/hz-pm-gen/src/main/java/com/hz/pm/gen/config/CodeGen.java b/hz-pm-gen/src/main/java/com/hz/pm/gen/config/CodeGen.java index 901952d..189fde4 100644 --- a/hz-pm-gen/src/main/java/com/hz/pm/gen/config/CodeGen.java +++ b/hz-pm-gen/src/main/java/com/hz/pm/gen/config/CodeGen.java @@ -55,7 +55,7 @@ public class CodeGen { } public static void main(String[] args) { - generate("WendyYang", "projectdeclared", PATH_YYD, "nd_project_review"); + generate("WendyYang", "sys", PATH_YYD, "nd_msg_call_record"); } }