@@ -4,11 +4,10 @@ import cn.hutool.core.lang.TypeReference; | |||||
import cn.hutool.http.HttpUtil; | import cn.hutool.http.HttpUtil; | ||||
import cn.hutool.json.JSONUtil; | import cn.hutool.json.JSONUtil; | ||||
import com.alibaba.fastjson.JSON; | import com.alibaba.fastjson.JSON; | ||||
import com.alibaba.fastjson.JSONArray; | |||||
import com.alibaba.fastjson.JSONObject; | import com.alibaba.fastjson.JSONObject; | ||||
import com.hz.pm.api.external.sms.dto.SmsDto; | 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.SmsReceipt; | ||||
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.external.sms.vo.SmsSendResponse; | ||||
import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
@@ -38,17 +37,15 @@ public class SmsServiceClient { | |||||
* @param result | * @param result | ||||
* @return | * @return | ||||
*/ | */ | ||||
public List<SmsReply> smsReply(String result) { | |||||
List<SmsReply> smsReplyList; | |||||
public SmsReplyResponse smsReply(String result) { | |||||
String refreshUrl = smsUrl + SMS_REPLY; | String refreshUrl = smsUrl + SMS_REPLY; | ||||
HashMap<String,Object> map = new HashMap<>(); | HashMap<String,Object> map = new HashMap<>(); | ||||
map.put("result",result); | map.put("result",result); | ||||
String responseResult = HttpUtil.post(refreshUrl, map); | String responseResult = HttpUtil.post(refreshUrl, map); | ||||
JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); | JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); | ||||
String fileData = responseJson.getString("data"); | String fileData = responseJson.getString("data"); | ||||
JSONArray result1 = JSON.parseObject(fileData).getJSONArray("result"); | |||||
smsReplyList = JSONObject.parseArray(result1.toJSONString(),SmsReply.class); | |||||
return smsReplyList; | |||||
return JSONUtil.toBean(fileData, new TypeReference<SmsReplyResponse>() { | |||||
}, false); | |||||
} | } | ||||
/** | /** | ||||
@@ -78,6 +75,11 @@ public class SmsServiceClient { | |||||
String responseResult = HttpUtil.post(refreshUrl, map); | String responseResult = HttpUtil.post(refreshUrl, map); | ||||
JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); | JSONObject responseJson = JSON.parseObject(responseResult, JSONObject.class); | ||||
String fileData = responseJson.getString("data"); | String fileData = responseJson.getString("data"); | ||||
JSONObject jsonObject = JSON.parseObject(fileData); | |||||
Map<String, Object> dataMap = jsonObject.getInnerMap(); | |||||
if(dataMap.get("result") instanceof Boolean){ | |||||
return SmsReceipt.builder().error(dataMap.get("error").toString()).build(); | |||||
} | |||||
return JSONObject.parseObject(fileData, SmsReceipt.class); | return JSONObject.parseObject(fileData, SmsReceipt.class); | ||||
} | } | ||||
@@ -1,8 +1,10 @@ | |||||
package com.hz.pm.api.external.sms.vo; | package com.hz.pm.api.external.sms.vo; | ||||
import lombok.Builder; | |||||
import lombok.Data; | import lombok.Data; | ||||
@Data | @Data | ||||
@Builder | |||||
public class SmsReceipt { | public class SmsReceipt { | ||||
// {"result":{"successPhone":["13721760288"]},"success":true,"message":"","rows":0,"sendTime":1703835660175} | // {"result":{"successPhone":["13721760288"]},"success":true,"message":"","rows":0,"sendTime":1703835660175} | ||||
@@ -15,4 +17,6 @@ public class SmsReceipt { | |||||
private Integer rows; | private Integer rows; | ||||
private Long sendTime; | private Long sendTime; | ||||
private String error; | |||||
} | } |
@@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModel; | |||||
import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
import lombok.Data; | import lombok.Data; | ||||
import java.util.List; | |||||
@Data | @Data | ||||
@ApiModel("短信回复响应内容") | @ApiModel("短信回复响应内容") | ||||
public class SmsReplyResponse { | public class SmsReplyResponse { | ||||
@@ -15,7 +17,7 @@ public class SmsReplyResponse { | |||||
private String errorPhone; | private String errorPhone; | ||||
@ApiModelProperty("回复内容") | @ApiModelProperty("回复内容") | ||||
private SmsReply result; | |||||
private List<SmsReply> result; | |||||
@ApiModelProperty("发送时间") | @ApiModelProperty("发送时间") | ||||
private Integer sendTime; | private Integer sendTime; | ||||
@@ -13,20 +13,20 @@ public interface MeetingMsgTemplateConst { | |||||
/** | /** | ||||
* 已结束:自动抽取结束,结束时给会议发起人发送浙政钉工作通知、短信:“注意,xxx会议自动抽取已结束,请及时确认是否召开会议”。 | * 已结束:自动抽取结束,结束时给会议发起人发送浙政钉工作通知、短信:“注意,xxx会议自动抽取已结束,请及时确认是否召开会议”。 | ||||
*/ | */ | ||||
String INVITE_END = "注意,%s会议自动抽取已结束,请及时确认是否召开会议"; | |||||
String INVITE_END = "【杭州数字信创】注意,%s会议自动抽取已结束,请及时确认是否召开会议"; | |||||
/** | /** | ||||
* 确认名单:尊敬的【姓名】专家您好,您于【确认时间】接受了信息化项目评审会议邀请,会议时间:【会议时间】,会议地点:【会议地点】。请准时参加评审会议。如有疑问请联系【联系人】(【联系方式】)。 | * 确认名单:尊敬的【姓名】专家您好,您于【确认时间】接受了信息化项目评审会议邀请,会议时间:【会议时间】,会议地点:【会议地点】。请准时参加评审会议。如有疑问请联系【联系人】(【联系方式】)。 | ||||
*/ | */ | ||||
String CONFIRMED_ROSTER = "尊敬的%s专家您好,您于%s接受了信息化项目评审会议邀请,会议时间:%s,会议地点:%s。请准时参加评审会议。如有疑问请联系%s(%s)。"; | |||||
String CONFIRMED_ROSTER = "【杭州数字信创】尊敬的%s专家您好,您于%s接受了信息化项目评审会议邀请,会议时间:%s,会议地点:%s。请准时参加评审会议。如有疑问请联系%s(%s)。"; | |||||
/** | /** | ||||
* 会议取消:尊敬的{name}专家,因会议取消说明,原定于开会时间的会议类型会议已取消。给您带来不便,敬请谅解。如有疑问请咨询会议联系人「会议联系人联系方式」。 | * 会议取消:尊敬的{name}专家,因会议取消说明,原定于开会时间的会议类型会议已取消。给您带来不便,敬请谅解。如有疑问请咨询会议联系人「会议联系人联系方式」。 | ||||
*/ | */ | ||||
String MEETING_CANCEL = "尊敬的%s专家,原定于%s的%s会议已取消。给您带来不便,敬请谅解。如有疑问请咨询会议联系人「%s」。"; | |||||
String MEETING_CANCEL = "【杭州数字信创】尊敬的%s专家,原定于%s的%s会议已取消。给您带来不便,敬请谅解。如有疑问请咨询会议联系人「%s」。"; | |||||
String EXPERT_LEAVE_RANDOM = "请注意,%s会议有专家请假,将重新抽取以替换该专家。"; | |||||
String EXPERT_LEAVE_RANDOM = "【杭州数字信创】请注意,%s会议有专家请假,将重新抽取以替换该专家。"; | |||||
String EXPERT_LEAVE_APPOINT = "请注意,%s会议的%s专家请假,请及时登录系统替换该专家。"; | |||||
String EXPERT_LEAVE_APPOINT = "【杭州数字信创】请注意,%s会议的%s专家请假,请及时登录系统替换该专家。"; | |||||
} | } |
@@ -1,11 +1,17 @@ | |||||
package com.hz.pm.api.meeting.controller; | package com.hz.pm.api.meeting.controller; | ||||
import com.ningdatech.basic.model.PagePo; | |||||
import com.ningdatech.basic.model.PageVo; | |||||
import com.ningdatech.log.annotation.WebLog; | |||||
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.SmsReceipt; | |||||
import com.hz.pm.api.external.sms.vo.SmsReplyResponse; | |||||
import com.hz.pm.api.external.sms.vo.SmsSendResponse; | |||||
import com.hz.pm.api.meeting.entity.req.MeetingCalenderReq; | import com.hz.pm.api.meeting.entity.req.MeetingCalenderReq; | ||||
import com.hz.pm.api.meeting.entity.vo.*; | import com.hz.pm.api.meeting.entity.vo.*; | ||||
import com.hz.pm.api.meeting.manage.DashboardManage; | import com.hz.pm.api.meeting.manage.DashboardManage; | ||||
import com.hz.pm.api.sms.constant.VoiceSmsTemplateConst; | |||||
import com.ningdatech.basic.model.PagePo; | |||||
import com.ningdatech.basic.model.PageVo; | |||||
import com.ningdatech.log.annotation.WebLog; | |||||
import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||
import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||
@@ -14,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | import org.springframework.web.bind.annotation.RestController; | ||||
import javax.validation.Valid; | import javax.validation.Valid; | ||||
import java.util.Collections; | |||||
import java.util.List; | import java.util.List; | ||||
/** | /** | ||||
@@ -32,6 +39,26 @@ public class ExpertDashboardController { | |||||
private final DashboardManage dashboardManage; | private final DashboardManage dashboardManage; | ||||
private final SmsServiceClient smsServiceClient; | |||||
@ApiOperation("短信发送") | |||||
@GetMapping("/sendSms") | |||||
public SmsDto<SmsSendResponse> test(String phone) { | |||||
return smsServiceClient.smsSend(VoiceSmsTemplateConst.EXPERT_INVITE, Collections.singletonList(phone)); | |||||
} | |||||
@ApiOperation("短信回复") | |||||
@GetMapping("/aplySms") | |||||
public SmsReplyResponse smsReply(String uuid) { | |||||
return smsServiceClient.smsReply(uuid); | |||||
} | |||||
@ApiOperation("短信回执") | |||||
@GetMapping("/status") | |||||
public SmsReceipt smsReceipt(String uuid) { | |||||
return smsServiceClient.smsReceipt(uuid); | |||||
} | |||||
@ApiOperation("会议日历") | @ApiOperation("会议日历") | ||||
@GetMapping("/meetingCalender") | @GetMapping("/meetingCalender") | ||||
@WebLog(value = "会议日历") | @WebLog(value = "会议日历") | ||||
@@ -102,4 +102,7 @@ public class Meeting implements Serializable { | |||||
@TableField(fill = FieldFill.INSERT_UPDATE) | @TableField(fill = FieldFill.INSERT_UPDATE) | ||||
private LocalDateTime updateOn; | private LocalDateTime updateOn; | ||||
@ApiModelProperty("通知方式:1电话 2短信") | |||||
private String notifyWay; | |||||
} | } |
@@ -43,8 +43,10 @@ public class MeetingExpert implements Serializable { | |||||
@ApiModelProperty("邀请规则ID") | @ApiModelProperty("邀请规则ID") | ||||
private Long ruleId; | private Long ruleId; | ||||
@ApiModelProperty("手机号") | |||||
private String mobile; | private String mobile; | ||||
@ApiModelProperty("专家名称") | |||||
private String expertName; | private String expertName; | ||||
@ApiModelProperty("当前状态") | @ApiModelProperty("当前状态") | ||||
@@ -61,6 +63,12 @@ public class MeetingExpert implements Serializable { | |||||
private String submitKey; | private String submitKey; | ||||
@ApiModelProperty("短信发送返回的uuid") | |||||
private String smsUuid; | |||||
@ApiModelProperty("通知方式:1电话 2短信") | |||||
private String notifyWay; | |||||
@TableField(fill = FieldFill.INSERT) | @TableField(fill = FieldFill.INSERT) | ||||
private Long createBy; | private Long createBy; | ||||
@@ -0,0 +1,54 @@ | |||||
package com.hz.pm.api.meeting.entity.domain; | |||||
import com.baomidou.mybatisplus.annotation.IdType; | |||||
import com.baomidou.mybatisplus.annotation.TableId; | |||||
import com.baomidou.mybatisplus.annotation.TableName; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import java.time.LocalDateTime; | |||||
/** | |||||
* @author wangrenkang | |||||
* @date 2024-02-20 18:14:29 | |||||
*/ | |||||
@Data | |||||
@Builder | |||||
@TableName("MEETING_EXPERT_SMS") | |||||
@ApiModel(value = "专家短信提醒记录") | |||||
public class MeetingExpertSms { | |||||
@TableId(type = IdType.AUTO) | |||||
private Long id; | |||||
/** | |||||
* 会议ID | |||||
*/ | |||||
private String meetingId; | |||||
/** | |||||
* 创建时间 | |||||
*/ | |||||
private LocalDateTime createOn; | |||||
/** | |||||
* 短信返回的 uuid | |||||
*/ | |||||
private String smsUuid; | |||||
/** | |||||
* 短信发送内容 | |||||
*/ | |||||
private String content; | |||||
/** | |||||
* 短信发送手机号 | |||||
*/ | |||||
private String phones; | |||||
/** | |||||
* 短信返回内容 | |||||
*/ | |||||
private String smsResult; | |||||
} |
@@ -85,4 +85,7 @@ public class MeetingBasicDTO { | |||||
@ApiModelProperty("相关材料") | @ApiModelProperty("相关材料") | ||||
private String attachFiles; | private String attachFiles; | ||||
@ApiModelProperty(value = "通知方式:1电话 2短信", example = "2") | |||||
private String notifyWay; | |||||
} | } |
@@ -0,0 +1,32 @@ | |||||
package com.hz.pm.api.meeting.entity.dto; | |||||
import com.hz.pm.api.external.sms.vo.SmsReply; | |||||
import io.swagger.annotations.ApiModel; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Builder; | |||||
import lombok.Data; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* @author wangrenkang | |||||
* @date 2024-02-21 14:24:32 | |||||
*/ | |||||
@Data | |||||
@Builder | |||||
@ApiModel("专家短信回复信息详情") | |||||
public class SmsReplyDetails { | |||||
@ApiModelProperty("回复1同意参加的手机号") | |||||
private Map<String, List<SmsReply>> accept; | |||||
@ApiModelProperty("回复2拒绝参加的手机号") | |||||
private Map<String, List<SmsReply>> reject; | |||||
@ApiModelProperty("回复其他内容的手机号") | |||||
private Map<String, List<SmsReply>> other; | |||||
@ApiModelProperty("发送失败的手机号") | |||||
private Map<String, List<String>> errorPhones; | |||||
} |
@@ -0,0 +1,39 @@ | |||||
package com.hz.pm.api.meeting.entity.enumeration; | |||||
import lombok.Getter; | |||||
import java.util.Arrays; | |||||
/** | |||||
* @author wangrenkang | |||||
* @date 2024-02-20 18:16:46 | |||||
*/ | |||||
@Getter | |||||
public enum ExpertNotifyTypeEnum { | |||||
/** | |||||
* 专家通知方式 | |||||
*/ | |||||
CALL("1", "电话通知"), | |||||
SMS("2", "短信通知"); | |||||
private final String code; | |||||
private final String name; | |||||
ExpertNotifyTypeEnum(String code, String name) { | |||||
this.code = code; | |||||
this.name = name; | |||||
} | |||||
public boolean eq(String code) { | |||||
return this.getCode().equals(code); | |||||
} | |||||
public static ExpertNotifyTypeEnum getByCode(String code) { | |||||
return Arrays.stream(values()) | |||||
.filter(w -> w.getCode().equals(code)) | |||||
.findFirst() | |||||
.orElseThrow(() -> new IllegalArgumentException("无效的通知方式")); | |||||
} | |||||
} |
@@ -107,4 +107,7 @@ public class MeetingDetailBasicVO { | |||||
@ApiModelProperty("会议结果附件") | @ApiModelProperty("会议结果附件") | ||||
private String resultAttachFiles; | private String resultAttachFiles; | ||||
@ApiModelProperty("通知方式:1电话 2短信") | |||||
private String notifyWay; | |||||
} | } |
@@ -3,17 +3,17 @@ package com.hz.pm.api.meeting.helper; | |||||
import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||
import com.alibaba.fastjson.JSON; | import com.alibaba.fastjson.JSON; | ||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||||
import com.ningdatech.basic.util.CollUtils; | |||||
import com.hz.pm.api.meeting.constant.MeetingMsgTemplateConst; | 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.Meeting; | ||||
import com.hz.pm.api.meeting.entity.domain.MeetingExpert; | 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.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.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.DingEmployeeInfo; | ||||
import com.hz.pm.api.organization.model.entity.DingOrganization; | import com.hz.pm.api.organization.model.entity.DingOrganization; | ||||
import com.hz.pm.api.organization.service.IDingEmployeeInfoService; | import com.hz.pm.api.organization.service.IDingEmployeeInfoService; | ||||
import com.hz.pm.api.organization.service.IDingOrganizationService; | 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.sms.utils.DateUtil; | ||||
import com.hz.pm.api.staging.enums.MsgTypeEnum; | import com.hz.pm.api.staging.enums.MsgTypeEnum; | ||||
import com.hz.pm.api.staging.service.INdWorkNoticeStagingService; | import com.hz.pm.api.staging.service.INdWorkNoticeStagingService; | ||||
@@ -22,8 +22,8 @@ import com.hz.pm.api.sys.service.INotifyService; | |||||
import com.hz.pm.api.todocenter.bean.entity.WorkNoticeInfo; | import com.hz.pm.api.todocenter.bean.entity.WorkNoticeInfo; | ||||
import com.hz.pm.api.user.model.entity.UserInfo; | import com.hz.pm.api.user.model.entity.UserInfo; | ||||
import com.hz.pm.api.user.service.IUserInfoService; | import com.hz.pm.api.user.service.IUserInfoService; | ||||
import com.ningdatech.basic.util.CollUtils; | |||||
import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; | import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; | ||||
import com.ningdatech.yxt.model.cmd.SubmitTaskCallCmd.SubmitTaskCallContext; | |||||
import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||
import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
import org.springframework.transaction.annotation.Transactional; | import org.springframework.transaction.annotation.Transactional; | ||||
@@ -53,6 +53,7 @@ public class MeetingCallOrMsgHelper { | |||||
private final IDingEmployeeInfoService dingEmployeeInfoService; | private final IDingEmployeeInfoService dingEmployeeInfoService; | ||||
private final IDingOrganizationService dingOrganizationService; | private final IDingOrganizationService dingOrganizationService; | ||||
private final INotifyService notifyService; | private final INotifyService notifyService; | ||||
private final ExpertCallResultRewriteTask expertCallResultRewriteTask; | |||||
private static String officialTime(LocalDateTime time) { | private static String officialTime(LocalDateTime time) { | ||||
return time.format(DateUtil.DTF_YMD_HM); | return time.format(DateUtil.DTF_YMD_HM); | ||||
@@ -174,20 +175,31 @@ public class MeetingCallOrMsgHelper { | |||||
* @param experts 待通知专家 | * @param experts 待通知专家 | ||||
* @author WendyYang | * @author WendyYang | ||||
**/ | **/ | ||||
public void callExpertByMeeting(Meeting meeting, List<MeetingExpert> experts) { | |||||
String voiceContent = String.format(VoiceSmsTemplateConst.EXPERT_INVITE, | |||||
meeting.getHoldOrg(), meeting.getName(), officialTime(meeting.getStartTime()), | |||||
meeting.getMeetingAddress()); | |||||
List<SubmitTaskCallContext> 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 callExpertByMeeting(Meeting meeting, List<MeetingExpert> experts) { | |||||
// String voiceContent = String.format(VoiceSmsTemplateConst.EXPERT_INVITE, | |||||
// meeting.getHoldOrg(), meeting.getName(), officialTime(meeting.getStartTime()), | |||||
// meeting.getMeetingAddress()); | |||||
// List<SubmitTaskCallContext> 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<MeetingExpert> 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)); | |||||
} | |||||
} | } | ||||
public void sendExpertLeaveMsg(MeetingExpert expert, Meeting meeting) { | public void sendExpertLeaveMsg(MeetingExpert expert, Meeting meeting) { | ||||
Long userId = meeting.getCreateBy(); | Long userId = meeting.getCreateBy(); | ||||
String msgContent; | String msgContent; | ||||
@@ -0,0 +1,69 @@ | |||||
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<SmsSendResponse> 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; | |||||
} | |||||
} |
@@ -1,7 +1,6 @@ | |||||
package com.hz.pm.api.meeting.helper; | package com.hz.pm.api.meeting.helper; | ||||
import com.ningdatech.yxt.client.YxtClient; | import com.ningdatech.yxt.client.YxtClient; | ||||
import com.ningdatech.yxt.constants.YxtSmsSignEnum; | |||||
import com.ningdatech.yxt.model.cmd.SendSmsCmd; | import com.ningdatech.yxt.model.cmd.SendSmsCmd; | ||||
import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; | import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; | ||||
import com.ningdatech.yxt.model.cmd.SubmitTaskCallResponse; | import com.ningdatech.yxt.model.cmd.SubmitTaskCallResponse; | ||||
@@ -36,7 +35,7 @@ public class YxtClientHelper { | |||||
public void sendSms(List<SendSmsContext> smsList) { | public void sendSms(List<SendSmsContext> smsList) { | ||||
SendSmsCmd cmd = new SendSmsCmd(); | SendSmsCmd cmd = new SendSmsCmd(); | ||||
cmd.setContextList(smsList); | cmd.setContextList(smsList); | ||||
cmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); | |||||
// cmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); | |||||
yxtClient.submitSmsTask(cmd); | yxtClient.submitSmsTask(cmd); | ||||
} | } | ||||
@@ -576,7 +576,9 @@ public class ExpertInviteManage { | |||||
expertInserts.add(expert); | expertInserts.add(expert); | ||||
}); | }); | ||||
} | } | ||||
meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertInserts); | |||||
// meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertInserts); | |||||
// 短信或电话提醒 | |||||
meetingCallOrMsgHelper.smsOrCallExpertByMeeting(meeting, expertInserts); | |||||
} | } | ||||
meetingExpertService.saveBatch(expertInserts); | meetingExpertService.saveBatch(expertInserts); | ||||
} | } | ||||
@@ -14,7 +14,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||||
import com.hz.pm.api.common.helper.RegionCacheHelper; | import com.hz.pm.api.common.helper.RegionCacheHelper; | ||||
import com.hz.pm.api.expert.entity.ExpertUserFullInfo; | import com.hz.pm.api.expert.entity.ExpertUserFullInfo; | ||||
import com.hz.pm.api.expert.service.IExpertReviewService; | |||||
import com.hz.pm.api.expert.service.IExpertUserFullInfoService; | import com.hz.pm.api.expert.service.IExpertUserFullInfoService; | ||||
import com.hz.pm.api.gov.service.IBelongOrgService; | import com.hz.pm.api.gov.service.IBelongOrgService; | ||||
import com.hz.pm.api.meeting.builder.ExpertInviteBuilder; | import com.hz.pm.api.meeting.builder.ExpertInviteBuilder; | ||||
@@ -36,8 +35,8 @@ import com.hz.pm.api.meeting.task.ExpertRandomInviteTask; | |||||
import com.hz.pm.api.meta.helper.DictionaryCache; | import com.hz.pm.api.meta.helper.DictionaryCache; | ||||
import com.hz.pm.api.meta.helper.TagCache; | import com.hz.pm.api.meta.helper.TagCache; | ||||
import com.hz.pm.api.organization.service.IDingOrganizationService; | import com.hz.pm.api.organization.service.IDingOrganizationService; | ||||
import com.hz.pm.api.projectlib.model.enumeration.ProjectStatusEnum; | |||||
import com.hz.pm.api.projectlib.model.entity.Project; | import com.hz.pm.api.projectlib.model.entity.Project; | ||||
import com.hz.pm.api.projectlib.model.enumeration.ProjectStatusEnum; | |||||
import com.hz.pm.api.projectlib.service.IProjectService; | import com.hz.pm.api.projectlib.service.IProjectService; | ||||
import com.hz.pm.api.sys.model.dto.RegionDTO; | import com.hz.pm.api.sys.model.dto.RegionDTO; | ||||
import com.hz.pm.api.user.security.model.UserInfoDetails; | import com.hz.pm.api.user.security.model.UserInfoDetails; | ||||
@@ -228,7 +227,9 @@ public class MeetingManage { | |||||
Assert.notNull(avoidInfo, "回避信息不能为空"); | Assert.notNull(avoidInfo, "回避信息不能为空"); | ||||
// 随机抽取的话则需进行抽取数量校验 | // 随机抽取的话则需进行抽取数量校验 | ||||
LocalDateTime now = LocalDateTime.now(); | LocalDateTime now = LocalDateTime.now(); | ||||
// 专家抽取(会议创建时抽取) | |||||
expertInviteManage.expertInviteByMeetingCreate(meeting, randomRules, avoidInfo); | expertInviteManage.expertInviteByMeetingCreate(meeting, randomRules, avoidInfo); | ||||
// 创建会议时添加抽取任务 | |||||
expertRandomInviteTask.addInviteTaskByMeetingCreate(meeting.getId(), now); | expertRandomInviteTask.addInviteTaskByMeetingCreate(meeting.getId(), now); | ||||
LambdaUpdateWrapper<Meeting> mUpdate = Wrappers.lambdaUpdate(Meeting.class) | LambdaUpdateWrapper<Meeting> mUpdate = Wrappers.lambdaUpdate(Meeting.class) | ||||
.set(Meeting::getInviteStatus, false) | .set(Meeting::getInviteStatus, false) | ||||
@@ -460,6 +461,7 @@ public class MeetingManage { | |||||
.resultDescription(meeting.getResultDescription()) | .resultDescription(meeting.getResultDescription()) | ||||
.resultAttachFiles(meeting.getResultAttachFiles()) | .resultAttachFiles(meeting.getResultAttachFiles()) | ||||
.remark(meeting.getRemark()) | .remark(meeting.getRemark()) | ||||
.notifyWay(meeting.getNotifyWay()) | |||||
.build(); | .build(); | ||||
if (Boolean.TRUE.equals(meeting.getIsInnerProject())) { | if (Boolean.TRUE.equals(meeting.getIsInnerProject())) { | ||||
List<MeetingInnerProject> innerProjects = meetingInnerProjectService.listByMeetingId(meetingId); | List<MeetingInnerProject> innerProjects = meetingInnerProjectService.listByMeetingId(meetingId); | ||||
@@ -0,0 +1,16 @@ | |||||
package com.hz.pm.api.meeting.mapper; | |||||
/** | |||||
* @author wangrenkang | |||||
* @date 2024-02-20 18:16:46 | |||||
*/ | |||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||||
import com.hz.pm.api.meeting.entity.domain.MeetingExpertSms; | |||||
import org.springframework.stereotype.Repository; | |||||
@Repository | |||||
public interface MeetingExpertSmsMapper extends BaseMapper<MeetingExpertSms> { | |||||
int insert(MeetingExpertSms meetingExpertSms); | |||||
} |
@@ -0,0 +1,10 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
<mapper namespace="com.hz.pm.api.meeting.mapper.MeetingExpertSmsMapper"> | |||||
<insert id="insert" parameterType="com.hz.pm.api.meeting.entity.domain.MeetingExpertSms"> | |||||
INSERT INTO HZ_PROJECT_MANAGEMENT1.MEETING_EXPERT_SMS | |||||
(MEETING_ID, CREATE_ON, SMS_UUID, CONTENT, PHONES, SMS_RESULT) | |||||
VALUES (#{meetingId}, #{createOn}, #{smsUuid}, #{content}, #{phones}, #{smsResult}); | |||||
</insert> | |||||
</mapper> |
@@ -0,0 +1,7 @@ | |||||
package com.hz.pm.api.meeting.service; | |||||
import com.baomidou.mybatisplus.extension.service.IService; | |||||
import com.hz.pm.api.meeting.entity.domain.MeetingExpertSms; | |||||
public interface IMeetingExpertSmsService extends IService<MeetingExpertSms> { | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.hz.pm.api.meeting.service.impl; | |||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||||
import com.hz.pm.api.meeting.entity.domain.MeetingExpertSms; | |||||
import com.hz.pm.api.meeting.mapper.MeetingExpertSmsMapper; | |||||
import com.hz.pm.api.meeting.service.IMeetingExpertSmsService; | |||||
import org.springframework.stereotype.Service; | |||||
@Service | |||||
public class MeetingExpertSmsServiceImpl extends ServiceImpl<MeetingExpertSmsMapper, MeetingExpertSms> implements IMeetingExpertSmsService { | |||||
} |
@@ -4,16 +4,26 @@ import cn.hutool.core.util.ObjectUtil; | |||||
import com.alibaba.fastjson.JSONObject; | import com.alibaba.fastjson.JSONObject; | ||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||||
import com.ningdatech.basic.util.StrPool; | |||||
import com.hz.pm.api.common.util.StrUtils; | 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.meeting.entity.domain.ExpertInviteRule; | 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.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.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.dto.YxtCallBackDTO; | ||||
import com.hz.pm.api.meeting.entity.enumeration.ExpertAttendStatusEnum; | 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.ExpertInviteTypeEnum; | ||||
import com.hz.pm.api.meeting.mapper.MeetingExpertSmsMapper; | |||||
import com.hz.pm.api.meeting.service.IExpertInviteRuleService; | import com.hz.pm.api.meeting.service.IExpertInviteRuleService; | ||||
import com.hz.pm.api.meeting.service.IMeetingExpertService; | 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.entity.SysMsgRecordDetail; | ||||
import com.ningdatech.yxt.service.ISysMsgRecordDetailService; | import com.ningdatech.yxt.service.ISysMsgRecordDetailService; | ||||
import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||
@@ -25,6 +35,8 @@ import javax.annotation.PostConstruct; | |||||
import java.time.Duration; | import java.time.Duration; | ||||
import java.time.Instant; | import java.time.Instant; | ||||
import java.time.LocalDateTime; | import java.time.LocalDateTime; | ||||
import java.time.ZoneId; | |||||
import java.time.format.DateTimeFormatter; | |||||
import java.time.temporal.ChronoUnit; | import java.time.temporal.ChronoUnit; | ||||
import java.util.*; | import java.util.*; | ||||
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
@@ -52,6 +64,9 @@ public class ExpertCallResultRewriteTask { | |||||
private final IMeetingExpertService meetingExpertService; | private final IMeetingExpertService meetingExpertService; | ||||
private final IExpertInviteRuleService inviteRuleService; | private final IExpertInviteRuleService inviteRuleService; | ||||
private final ISysMsgRecordDetailService msgRecordDetailService; | private final ISysMsgRecordDetailService msgRecordDetailService; | ||||
private final SmsServiceClient smsServiceClient; | |||||
private final MeetingExpertSmsMapper meetingExpertSmsMapper; | |||||
private final static int MINUTES_CALL_RESULT_FEEDBACK = 15; | private final static int MINUTES_CALL_RESULT_FEEDBACK = 15; | ||||
private static final String AGREE_KEY = "1"; | private static final String AGREE_KEY = "1"; | ||||
@@ -62,10 +77,65 @@ public class ExpertCallResultRewriteTask { | |||||
log.warn("随机邀请已关闭……"); | log.warn("随机邀请已关闭……"); | ||||
return; | return; | ||||
} | } | ||||
Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES); | |||||
// 处理电话结果回填 | // 处理电话结果回填 | ||||
Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES); | |||||
Duration fixedRate = Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()); | Duration fixedRate = Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate()); | ||||
scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, fixedRate); | |||||
// scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, fixedRate); | |||||
// 处理短信结果回填 | |||||
Instant smsStartTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES); | |||||
scheduler.scheduleAtFixedRate(this::expertSmsReply, smsStartTime, fixedRate); | |||||
} | |||||
public void expertSmsReply() { | |||||
log.info("开始执行短信回复查询任务:{}", Thread.currentThread().getName()); | |||||
// 查询所有邀请的专家信息 状态为通话中的 | |||||
LambdaQueryWrapper<MeetingExpert> meQuery = Wrappers.lambdaQuery(MeetingExpert.class) | |||||
.eq(MeetingExpert::getStatus, NOTICING.getCode()) | |||||
.eq(MeetingExpert::getInviteType, ExpertInviteTypeEnum.RANDOM.getCode()); | |||||
List<MeetingExpert> experts = meetingExpertService.list(meQuery); | |||||
if (experts.isEmpty()) { | |||||
log.info("暂无短信回复任务执行"); | |||||
return; | |||||
} | |||||
// 所有随机邀请的短信Uuid | |||||
List<String> smsUuids = experts.stream() | |||||
.map(expert -> expert.getSmsUuid()) | |||||
.filter(uuid -> uuid != null) | |||||
.distinct() | |||||
.collect(Collectors.toList()); | |||||
Set<Long> randomRuleIds = experts.stream() | |||||
.map(MeetingExpert::getRuleId).collect(Collectors.toSet()); | |||||
// 查询随机邀请回调等待时间 | |||||
Map<Long, Integer> callbackMinutes = new HashMap<>(randomRuleIds.size()); | |||||
if (!randomRuleIds.isEmpty()) { | |||||
List<ExpertInviteRule> inviteRules = inviteRuleService.listByIds(randomRuleIds); | |||||
inviteRules.forEach(expert -> { | |||||
RandomInviteRuleDTO rule = JSONObject.parseObject(expert.getInviteRule(), RandomInviteRuleDTO.class); | |||||
callbackMinutes.put(expert.getId(), rule.getWaitForCallbackMinutes()); | |||||
}); | |||||
} | |||||
// 获取专家回复内容 | |||||
SmsReplyDetails smsReplyDetails = viewSmsReplies(smsUuids); | |||||
List<MeetingExpert> updates = new ArrayList<>(); | |||||
for (MeetingExpert expert : experts) { | |||||
Integer minutes = ObjectUtil.defaultIfNull(callbackMinutes.get(expert.getRuleId()), MINUTES_CALL_RESULT_FEEDBACK); | |||||
// 判断回复状态 | |||||
Optional<Integer> 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); | |||||
} | } | ||||
@@ -171,4 +241,138 @@ public class ExpertCallResultRewriteTask { | |||||
return Optional.of(status.getCode()); | return Optional.of(status.getCode()); | ||||
} | } | ||||
private static Optional<Integer> getStatusByMsgRecordDetail(SmsReplyDetails smsReplyDetails, int minutes, MeetingExpert expert) { | |||||
Map<String, List<SmsReply>> accept = smsReplyDetails.getAccept() //回复1同意参加的手机号 | |||||
, reject = smsReplyDetails.getReject() //回复2拒绝参加的手机号 | |||||
, other = smsReplyDetails.getOther(); //回复其他的专家 | |||||
Map<String, List<String>> 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<String> errorPhoneList = errorPhones.get(expert.getSmsUuid()); | |||||
if(ObjectUtil.isNotEmpty(errorPhoneList) && errorPhoneList.contains(expert.getMobile())){ | |||||
// 未应答 | |||||
status = UNANSWERED; | |||||
} | |||||
if(ObjectUtil.isNotEmpty(accept)) { | |||||
List<SmsReply> 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<SmsReply> 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<SmsReply> 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<MeetingExpert> 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<String> phones = expertMeetings.stream().map(obj -> obj.getMobile()).collect(Collectors.toList()); | |||||
SmsDto<SmsSendResponse> 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<String> uuids) { | |||||
Map<String, List<SmsReply>> accept = new HashMap<>() | |||||
, reject = new HashMap<>() | |||||
, other = new HashMap<>(); | |||||
Map<String, List<String>> errorPhones = new HashMap<>(); | |||||
uuids.forEach(uuid -> { | |||||
SmsReplyResponse response = smsServiceClient.smsReply(uuid); | |||||
List<SmsReply> 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; | |||||
} | |||||
} | } |
@@ -191,6 +191,7 @@ public class ExpertRandomInviteTask { | |||||
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> { | ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(() -> { | ||||
ExpertRandomInviteTask bean = SpringContextHolder.getBean(ExpertRandomInviteTask.class); | ExpertRandomInviteTask bean = SpringContextHolder.getBean(ExpertRandomInviteTask.class); | ||||
try { | try { | ||||
// 抽取专家 | |||||
bean.invite(meetingId, reInvite, tsTime); | bean.invite(meetingId, reInvite, tsTime); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
log.error("执行专家邀请任务异常:{}", meetingId, e); | log.error("执行专家邀请任务异常:{}", meetingId, e); | ||||
@@ -267,7 +268,9 @@ public class ExpertRandomInviteTask { | |||||
expert.setStatus(ExpertAttendStatusEnum.NOTICING.getCode()); | expert.setStatus(ExpertAttendStatusEnum.NOTICING.getCode()); | ||||
return expert; | return expert; | ||||
}); | }); | ||||
meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertMeetings); | |||||
// meetingCallOrMsgHelper.callExpertByMeeting(meeting, expertMeetings); | |||||
// 短信或电话提醒 | |||||
meetingCallOrMsgHelper.smsOrCallExpertByMeeting(meeting, expertMeetings); | |||||
log.info("会议:{} 后台抽取专家:{}名", meetingId, expertMeetings.size()); | log.info("会议:{} 后台抽取专家:{}名", meetingId, expertMeetings.size()); | ||||
meetingExpertService.saveBatch(expertMeetings); | meetingExpertService.saveBatch(expertMeetings); | ||||
} else { | } else { | ||||
@@ -26,6 +26,10 @@ public class RandomInviteProperties { | |||||
*/ | */ | ||||
private Integer resultRewriteFixedRate = 2; | private Integer resultRewriteFixedRate = 2; | ||||
/** | /** | ||||
* 短信结果回填执行频率(分钟) | |||||
*/ | |||||
private Integer smsResultRewriteFixedRate = 1; | |||||
/** | |||||
* 随机邀请延迟执行(分钟) | * 随机邀请延迟执行(分钟) | ||||
*/ | */ | ||||
private Integer inviteDelay = 2; | private Integer inviteDelay = 2; | ||||
@@ -16,16 +16,16 @@ public class VoiceSmsTemplateConst { | |||||
/** | /** | ||||
* 短信登陆验证码 | * 短信登陆验证码 | ||||
*/ | */ | ||||
public static final String SMS_VERIFY_CODE = "验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。"; | |||||
public static final String SMS_VERIFY_CODE = "【杭州数字信创】验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。"; | |||||
/** | /** | ||||
* 社会专家报名 | * 社会专家报名 | ||||
*/ | */ | ||||
public static final String EXPERT_REGISTER = "专家报名验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。"; | |||||
public static final String EXPERT_REGISTER = "【杭州数字信创】专家报名验证码:%s(有效期为%s分钟),请勿泄露给他人,如非本人操作,请忽略此信息。"; | |||||
/** | /** | ||||
* 专家电话通知语音模版 | * 专家电话通知语音模版 | ||||
*/ | */ | ||||
public static final String EXPERT_INVITE = "尊敬的专家您好,%s现邀请您作为专家参加%s会议,会议时间:%s,会议地点:%s。 确认参加请按 1,拒绝参加请按 2。请您选择"; | |||||
public static final String EXPERT_INVITE = "【杭州数字信创】尊敬的专家您好,%s现邀请您作为专家参加%s会议,会议时间:%s,会议地点:%s。 确认参加请按 1,拒绝参加请按 2。请您选择"; | |||||
} | } |
@@ -2,17 +2,16 @@ package com.hz.pm.api.sms.manage; | |||||
import cn.hutool.core.util.PhoneUtil; | import cn.hutool.core.util.PhoneUtil; | ||||
import cn.hutool.core.util.RandomUtil; | import cn.hutool.core.util.RandomUtil; | ||||
import com.ningdatech.basic.exception.BizException; | |||||
import com.ningdatech.cache.model.cache.CacheKey; | |||||
import com.ningdatech.cache.repository.CachePlusOps; | |||||
import com.hz.pm.api.sms.constant.VerificationCodeType; | import com.hz.pm.api.sms.constant.VerificationCodeType; | ||||
import com.hz.pm.api.sms.constant.VoiceSmsTemplateConst; | import com.hz.pm.api.sms.constant.VoiceSmsTemplateConst; | ||||
import com.hz.pm.api.sms.model.dto.VerifyCodeCacheDTO; | import com.hz.pm.api.sms.model.dto.VerifyCodeCacheDTO; | ||||
import com.hz.pm.api.sms.model.po.ReqVerificationCodePO; | import com.hz.pm.api.sms.model.po.ReqVerificationCodePO; | ||||
import com.hz.pm.api.sms.utils.DateUtil; | import com.hz.pm.api.sms.utils.DateUtil; | ||||
import com.hz.pm.api.sms.utils.SmsRedisKeyUtils; | import com.hz.pm.api.sms.utils.SmsRedisKeyUtils; | ||||
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.client.YxtClient; | ||||
import com.ningdatech.yxt.constants.YxtSmsSignEnum; | |||||
import com.ningdatech.yxt.model.cmd.SendSmsCmd; | import com.ningdatech.yxt.model.cmd.SendSmsCmd; | ||||
import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; | import com.ningdatech.yxt.model.cmd.SendSmsCmd.SendSmsContext; | ||||
import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||
@@ -70,7 +69,7 @@ public class VerificationCodeManage { | |||||
sendSmsCtx.setReceiveNumber(req.getMobile()); | sendSmsCtx.setReceiveNumber(req.getMobile()); | ||||
sendSmsCtx.setContent(String.format(VoiceSmsTemplateConst.SMS_VERIFY_CODE, code, codeType.getExpireTime())); | sendSmsCtx.setContent(String.format(VoiceSmsTemplateConst.SMS_VERIFY_CODE, code, codeType.getExpireTime())); | ||||
sendSmsCmd.setContextList(Collections.singletonList(sendSmsCtx)); | sendSmsCmd.setContextList(Collections.singletonList(sendSmsCtx)); | ||||
sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); | |||||
// sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); | |||||
} | } | ||||
break; | break; | ||||
case EXPERT_REGISTER: { | case EXPERT_REGISTER: { | ||||
@@ -78,7 +77,7 @@ public class VerificationCodeManage { | |||||
sendSmsCtx.setReceiveNumber(req.getMobile()); | sendSmsCtx.setReceiveNumber(req.getMobile()); | ||||
sendSmsCtx.setContent(String.format(VoiceSmsTemplateConst.EXPERT_REGISTER, code, codeType.getExpireTime())); | sendSmsCtx.setContent(String.format(VoiceSmsTemplateConst.EXPERT_REGISTER, code, codeType.getExpireTime())); | ||||
sendSmsCmd.setContextList(Collections.singletonList(sendSmsCtx)); | sendSmsCmd.setContextList(Collections.singletonList(sendSmsCtx)); | ||||
sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); | |||||
// sendSmsCmd.setSmsSignEnum(YxtSmsSignEnum.LS_BIG_DATA_BUREAU); | |||||
} | } | ||||
break; | break; | ||||
default: | default: | ||||