mirror of https://gitee.com/topiam/eiam
⚡ 消息发送优化
parent
1b9f750450
commit
73c940111d
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management Program
|
||||
* Copyright © 2020-2023 TopIAM (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.message.mail;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 消息事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Getter
|
||||
public class MailMsgEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private final MailType type;
|
||||
/**
|
||||
* 接收人
|
||||
*/
|
||||
private final String receiver;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private final Map<String, Object> parameter;
|
||||
|
||||
public MailMsgEvent(MailType type, String receiver, Map<String, Object> parameter) {
|
||||
super(type);
|
||||
this.type = type;
|
||||
this.receiver = receiver;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management Program
|
||||
* Copyright © 2020-2023 TopIAM (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.message.mail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.topiam.employee.common.entity.message.MailSendRecordEntity;
|
||||
import cn.topiam.employee.common.entity.setting.MailTemplateEntity;
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.exception.MailMessageSendException;
|
||||
import cn.topiam.employee.common.message.mail.MailProviderSend;
|
||||
import cn.topiam.employee.common.message.mail.SendMailRequest;
|
||||
import cn.topiam.employee.common.repository.message.MailSendRecordRepository;
|
||||
import cn.topiam.employee.core.message.MsgVariable;
|
||||
import cn.topiam.employee.core.setting.constant.MessageSettingConstants;
|
||||
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import static org.apache.commons.codec.CharEncoding.UTF_8;
|
||||
import static org.springframework.web.util.HtmlUtils.htmlUnescape;
|
||||
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.COLON;
|
||||
|
||||
/**
|
||||
* 消息通知事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Async
|
||||
@Component
|
||||
public class MailMsgEventListener implements ApplicationListener<MailMsgEvent> {
|
||||
private final Logger logger = LoggerFactory.getLogger(MailMsgEventListener.class);
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull MailMsgEvent event) {
|
||||
// 邮件通知类型
|
||||
MailType type = event.getType();
|
||||
String content = htmlUnescape(MailUtils.readEmailContent(type.getContent()));
|
||||
String subject = type.getSubject();
|
||||
String sender = type.getSender();
|
||||
try {
|
||||
Map<String, Object> parameter = event.getParameter();
|
||||
// 判断是否存在自定义
|
||||
MailTemplateEntity templateEntity = (MailTemplateEntity) redisTemplate.opsForValue()
|
||||
.get(MessageSettingConstants.SETTING_EMAIL_TEMPLATE_CACHE_NAME + COLON
|
||||
+ type.getCode());
|
||||
if (!Objects.isNull(templateEntity)) {
|
||||
content = htmlUnescape(templateEntity.getContent());
|
||||
subject = templateEntity.getSubject();
|
||||
sender = templateEntity.getSender();
|
||||
}
|
||||
// 主题
|
||||
StringWriter themeStringWriter = new StringWriter();
|
||||
Template themeTpl = createTemplate(type.getCode() + "-theme", subject);
|
||||
themeTpl.process(parameter, themeStringWriter);
|
||||
// 测试邮件
|
||||
if (parameter.containsKey(MsgVariable.TEST)) {
|
||||
String test = parameter.get(MsgVariable.TEST).toString();
|
||||
themeStringWriter.append(test);
|
||||
}
|
||||
// 内容
|
||||
StringWriter contentStringWriter = new StringWriter();
|
||||
Template contentTpl = createTemplate(type.getCode() + "-content", content);
|
||||
contentTpl.process(parameter, contentStringWriter);
|
||||
// 发送邮件
|
||||
//@formatter:off
|
||||
SendMailRequest params = new SendMailRequest()
|
||||
// 发送人
|
||||
.setSender(sender)
|
||||
// 接收人
|
||||
.setReceiver(event.getReceiver())
|
||||
// 主题
|
||||
.setSubject(themeStringWriter.toString())
|
||||
// 内容
|
||||
.setBody(contentStringWriter.toString());
|
||||
//@formatter:on
|
||||
// 发送邮件
|
||||
mailProviderSend.sendMailHtml(params);
|
||||
// 保存发送记录
|
||||
MailSendRecordEntity record = new MailSendRecordEntity();
|
||||
//@formatter:off
|
||||
record.setSender(event.getReceiver())
|
||||
.setSubject(params.getSubject())
|
||||
.setContent(params.getBody())
|
||||
.setProvider(mailProviderSend.getProvider())
|
||||
.setType(event.getType())
|
||||
.setReceiver(event.getReceiver())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setSuccess(true);
|
||||
//@formatter:on
|
||||
mailSendRecordRepository.save(record);
|
||||
} catch (Exception e) {
|
||||
logger.error("邮件信息发送失败: {}", e.getMessage());
|
||||
throw new MailMessageSendException("邮件信息发送失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模板
|
||||
*
|
||||
* @param name {@link String} 模板名称
|
||||
* @param templateContent {@link String} 模板内容
|
||||
* @return {@link Template}
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private Template createTemplate(String name, String templateContent) throws IOException {
|
||||
try {
|
||||
Template template = freeMarkerConfiguration.getTemplate(name, UTF_8);
|
||||
if (template != null) {
|
||||
if (template.toString().trim().equals(templateContent.trim())) {
|
||||
return template;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
// 以下操作不是线程安全,要加上同步
|
||||
synchronized (this) {
|
||||
// 获取模板加载器
|
||||
TemplateLoader templateLoader = freeMarkerConfiguration.getTemplateLoader();
|
||||
if (templateLoader instanceof StringTemplateLoader) {
|
||||
// 如果加载器已经是字符串加载器,则在原来的加载器上put一个新的模板
|
||||
((StringTemplateLoader) templateLoader).putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(templateLoader);
|
||||
} else {
|
||||
// 如果原来的模板加载器不是字符串的(默认是文件加载器),则新建
|
||||
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
|
||||
stringTemplateLoader.putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(stringTemplateLoader);
|
||||
}
|
||||
// 这里要清一下缓存,不然下面可能会获取不到模板
|
||||
freeMarkerConfiguration.clearTemplateCache();
|
||||
|
||||
return freeMarkerConfiguration.getTemplate(name, UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MailSend
|
||||
*/
|
||||
private final MailProviderSend mailProviderSend;
|
||||
|
||||
/**
|
||||
* MailSendRecordRepository
|
||||
*/
|
||||
private final MailSendRecordRepository mailSendRecordRepository;
|
||||
/**
|
||||
* redis
|
||||
*/
|
||||
private final RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private final Configuration freeMarkerConfiguration;
|
||||
|
||||
public MailMsgEventListener(MailProviderSend mailProviderSend,
|
||||
MailSendRecordRepository mailSendRecordRepository,
|
||||
RedisTemplate<Object, Object> redisTemplate) {
|
||||
this.mailProviderSend = mailProviderSend;
|
||||
this.mailSendRecordRepository = mailSendRecordRepository;
|
||||
this.redisTemplate = redisTemplate;
|
||||
freeMarkerConfiguration = new Configuration(Configuration.VERSION_2_3_31);
|
||||
//设置模板加载文件夹
|
||||
freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/mail/content");
|
||||
}
|
||||
|
||||
}
|
|
@ -23,15 +23,14 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.message.enums.MessageType;
|
||||
import cn.topiam.employee.common.message.mail.MailProviderConfig;
|
||||
import cn.topiam.employee.core.mq.NoticeMessagePublisher;
|
||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
|
||||
|
@ -53,10 +52,11 @@ import static cn.topiam.employee.support.constant.EiamConstants.DEFAULT_DATE_TIM
|
|||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class MailMsgEventPublish {
|
||||
|
||||
/**
|
||||
* NoticeMessagePublisher
|
||||
* ApplicationEventPublisher
|
||||
*/
|
||||
private final NoticeMessagePublisher noticeMessageProducer;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
/**
|
||||
* 发布验证代码
|
||||
|
@ -101,7 +101,7 @@ public class MailMsgEventPublish {
|
|||
parameter.put(USER_EMAIL, receiver);
|
||||
// publish event
|
||||
ObjectMapper objectMapper = ApplicationContextHelp.getBean(ObjectMapper.class);
|
||||
noticeMessageProducer.sendNotice(MessageType.MAIL,
|
||||
objectMapper.writeValueAsString(new MailMessage(type, receiver, parameter)));
|
||||
applicationEventPublisher.publishEvent(new MailMsgEvent(type, receiver, parameter));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management Program
|
||||
* Copyright © 2020-2023 TopIAM (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.message.sms;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import cn.topiam.employee.common.enums.SmsType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 短信消息事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Getter
|
||||
public class SmsMsgEvent extends ApplicationEvent {
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private final SmsType type;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private final Map<String, String> parameter;
|
||||
|
||||
public SmsMsgEvent(SmsType type, Map<String, String> parameter) {
|
||||
super(parameter);
|
||||
this.type = type;
|
||||
this.parameter = parameter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management Program
|
||||
* Copyright © 2020-2023 TopIAM (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.message.sms;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
|
||||
import cn.topiam.employee.common.entity.message.SmsSendRecordEntity;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.exception.SmsMessageSendException;
|
||||
import cn.topiam.employee.common.message.sms.SendSmsRequest;
|
||||
import cn.topiam.employee.common.message.sms.SmsProviderSend;
|
||||
import cn.topiam.employee.common.message.sms.SmsResponse;
|
||||
import cn.topiam.employee.common.repository.message.SmsSendRecordRepository;
|
||||
|
||||
/**
|
||||
* 短信消息通知事件
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2021/9/25 21:07
|
||||
*/
|
||||
@Async
|
||||
@Component
|
||||
public class SmsMsgEventListener implements ApplicationListener<SmsMsgEvent> {
|
||||
/**
|
||||
* Logger
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(SmsMsgEventListener.class);
|
||||
|
||||
/**
|
||||
* 发送事件通知
|
||||
*
|
||||
* @param event {@link SmsMsgEvent}
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull SmsMsgEvent event) {
|
||||
SendSmsRequest smsParam = new SendSmsRequest();
|
||||
try {
|
||||
//@formatter:off
|
||||
// 手机号
|
||||
smsParam.setPhone(event.getParameter().get(SmsMsgEventPublish.PHONE));
|
||||
// 模版编码
|
||||
smsParam.setTemplate(event.getParameter().get(SmsMsgEventPublish.TEMPLATE_CODE));
|
||||
// Content 记录参数值
|
||||
String content = event.getParameter().get(SmsMsgEventPublish.CONTENT);
|
||||
// 移除手机号,模版编码和Content
|
||||
event.getParameter().remove(SmsMsgEventPublish.PHONE);
|
||||
event.getParameter().remove(SmsMsgEventPublish.TEMPLATE_CODE);
|
||||
event.getParameter().remove(SmsMsgEventPublish.CONTENT);
|
||||
// 短信模版参数
|
||||
smsParam.setParameters(event.getParameter());
|
||||
|
||||
//@formatter:on
|
||||
SmsResponse send = smsProviderSend.send(smsParam);
|
||||
// 保存发送记录
|
||||
if (!Objects.isNull(send)) {
|
||||
//@formatter:off
|
||||
SmsSendRecordEntity record = new SmsSendRecordEntity()
|
||||
.setContent(content)
|
||||
.setResult(send.getMessage())
|
||||
.setSuccess(send.getSuccess())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setProvider(send.getProvider())
|
||||
.setCategory(MessageCategory.CODE)
|
||||
.setType(event.getType())
|
||||
.setPhone(smsParam.getPhone());
|
||||
record.setRemark(JSON.toJSONString(send));
|
||||
if (!send.getSuccess()) {
|
||||
logger.error("发送短信失败: params: {}, response: {}", smsParam, send);
|
||||
}
|
||||
//@formatter:on
|
||||
smsSendLogRepository.save(record);
|
||||
} else {
|
||||
logger.error("发送短信失败,返回值为空: params: {}, ", smsParam);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("发送短信消息异常 params:{}, error: {}", smsParam, e.getMessage());
|
||||
throw new SmsMessageSendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SmsSend
|
||||
*/
|
||||
private final SmsProviderSend smsProviderSend;
|
||||
|
||||
private final SmsSendRecordRepository smsSendLogRepository;
|
||||
|
||||
public SmsMsgEventListener(SmsProviderSend smsProviderSend,
|
||||
SmsSendRecordRepository smsSendLogRepository) {
|
||||
this.smsProviderSend = smsProviderSend;
|
||||
this.smsSendLogRepository = smsSendLogRepository;
|
||||
}
|
||||
}
|
|
@ -23,18 +23,15 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.topiam.employee.common.entity.setting.config.SmsConfig;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.enums.SmsType;
|
||||
import cn.topiam.employee.common.message.enums.MessageType;
|
||||
import cn.topiam.employee.core.mq.NoticeMessagePublisher;
|
||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||
import cn.topiam.employee.support.exception.TopIamException;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
@ -55,18 +52,13 @@ import static cn.topiam.employee.core.message.MsgVariable.VERIFY_CODE;
|
|||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class SmsMsgEventPublish {
|
||||
public static final String TEMPLATE_CODE = "template_code";
|
||||
public static final String TEMPLATE_CODE = "template_code";
|
||||
|
||||
public static final String CONTENT = "content";
|
||||
public static final String CONTENT = "content";
|
||||
|
||||
/**
|
||||
* NoticeMessagePublisher
|
||||
*/
|
||||
private final NoticeMessagePublisher noticeMessageProducer;
|
||||
public static final String PHONE = "phone";
|
||||
|
||||
public static final String PHONE = "phone";
|
||||
|
||||
public static final String USERNAME = "username";
|
||||
public static final String USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 发布验证代码
|
||||
|
@ -110,9 +102,11 @@ public class SmsMsgEventPublish {
|
|||
parameter.put(TEMPLATE_CODE, templateConfig.getCode());
|
||||
parameter.put(CONTENT, JSON.toJSONString(parameter));
|
||||
// publish event
|
||||
ObjectMapper objectMapper = ApplicationContextHelp.getBean(ObjectMapper.class);
|
||||
noticeMessageProducer.sendNotice(MessageType.SMS,
|
||||
objectMapper.writeValueAsString(new SmsMessage(type, parameter)));
|
||||
applicationEventPublisher.publishEvent(new SmsMsgEvent(type, parameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplicationEventPublisher
|
||||
*/
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
}
|
||||
|
|
|
@ -1,338 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import cn.topiam.employee.common.entity.message.MailSendRecordEntity;
|
||||
import cn.topiam.employee.common.entity.message.SmsSendRecordEntity;
|
||||
import cn.topiam.employee.common.entity.setting.MailTemplateEntity;
|
||||
import cn.topiam.employee.common.enums.MailType;
|
||||
import cn.topiam.employee.common.enums.MessageCategory;
|
||||
import cn.topiam.employee.common.exception.MailMessageSendException;
|
||||
import cn.topiam.employee.common.exception.SmsMessageSendException;
|
||||
import cn.topiam.employee.common.message.mail.MailNoneProviderSend;
|
||||
import cn.topiam.employee.common.message.mail.MailProviderSend;
|
||||
import cn.topiam.employee.common.message.mail.SendMailRequest;
|
||||
import cn.topiam.employee.common.message.sms.SendSmsRequest;
|
||||
import cn.topiam.employee.common.message.sms.SmsNoneProviderSend;
|
||||
import cn.topiam.employee.common.message.sms.SmsProviderSend;
|
||||
import cn.topiam.employee.common.message.sms.SmsResponse;
|
||||
import cn.topiam.employee.common.repository.message.MailSendRecordRepository;
|
||||
import cn.topiam.employee.common.repository.message.SmsSendRecordRepository;
|
||||
import cn.topiam.employee.core.message.MsgVariable;
|
||||
import cn.topiam.employee.core.message.mail.MailMessage;
|
||||
import cn.topiam.employee.core.message.mail.MailUtils;
|
||||
import cn.topiam.employee.core.message.sms.SmsMessage;
|
||||
import cn.topiam.employee.core.message.sms.SmsMsgEventPublish;
|
||||
import cn.topiam.employee.core.setting.constant.MessageSettingConstants;
|
||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import static org.apache.commons.codec.CharEncoding.UTF_8;
|
||||
import static org.springframework.web.util.HtmlUtils.htmlUnescape;
|
||||
|
||||
import static cn.topiam.employee.core.mq.AbstractMessagePublisher.NOTICE_MAIL;
|
||||
import static cn.topiam.employee.core.mq.AbstractMessagePublisher.NOTICE_SMS;
|
||||
import static cn.topiam.employee.support.constant.EiamConstants.COLON;
|
||||
|
||||
/**
|
||||
* 短信/邮件消息监听器
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/5/30 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NoticeMessageListener extends AbstractMessageListener {
|
||||
|
||||
/**
|
||||
* 接收消息
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
* @param headers {@link Map}
|
||||
*/
|
||||
@Override
|
||||
@RabbitListener(queues = { NOTICE_SMS, NOTICE_MAIL }, ackMode = "MANUAL")
|
||||
@RabbitHandler()
|
||||
public void onMessage(Message message, Channel channel, @Payload String body,
|
||||
@Headers Map<String, Object> headers) throws IOException {
|
||||
super.onMessage(message, channel, body, headers);
|
||||
log.info("异步接收ES用户信息入参: [{}]", message);
|
||||
sendNotice(message, channel, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信邮件通知消息数据
|
||||
*
|
||||
* @param message {@link Message}
|
||||
* @param channel {@link Channel}
|
||||
* @param body {@link String}
|
||||
*/
|
||||
private String sendNotice(Message message, Channel channel, String body) throws IOException {
|
||||
try {
|
||||
String queueName = message.getMessageProperties().getConsumerQueue();
|
||||
if (Objects.isNull(body)) {
|
||||
log.warn("接收短信/邮件通知消息内容为空:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
return "接收短信/邮件通知消息内容为空";
|
||||
}
|
||||
log.info("接收通知消息:[{}]", body);
|
||||
ObjectMapper objectMapper = ApplicationContextHelp.getBean(ObjectMapper.class);
|
||||
if (queueName.equals(NOTICE_SMS)) {
|
||||
sendSms(objectMapper.readValue(body, SmsMessage.class));
|
||||
} else if (queueName.equals(NOTICE_MAIL)) {
|
||||
sendMail(objectMapper.readValue(body, MailMessage.class));
|
||||
}
|
||||
log.info("处理发送通知完成:[{}]", message.getMessageProperties().getDeliveryTag());
|
||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
||||
return "处理发送通知成功";
|
||||
} catch (Exception e) {
|
||||
log.error("处理发送通知消息出现异常: MessageProperties: [{}], message:[{}]",
|
||||
message.getMessageProperties(), body, e);
|
||||
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
|
||||
return "处理发送通知失败";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信通知
|
||||
*
|
||||
* @param smsMessage {@link SmsMessage}
|
||||
*/
|
||||
private void sendSms(@NonNull SmsMessage smsMessage) {
|
||||
if (smsProviderSend instanceof SmsNoneProviderSend) {
|
||||
throw new SmsMessageSendException("暂未配置短信服务");
|
||||
}
|
||||
SendSmsRequest smsParam = new SendSmsRequest();
|
||||
try {
|
||||
//@formatter:off
|
||||
// 手机号
|
||||
smsParam.setPhone(smsMessage.getParameter().get(SmsMsgEventPublish.PHONE));
|
||||
// 模版编码
|
||||
smsParam.setTemplate(smsMessage.getParameter().get(SmsMsgEventPublish.TEMPLATE_CODE));
|
||||
// Content 记录参数值
|
||||
String content = smsMessage.getParameter().get(SmsMsgEventPublish.CONTENT);
|
||||
// 移除手机号,模版编码和Content
|
||||
smsMessage.getParameter().remove(SmsMsgEventPublish.PHONE);
|
||||
smsMessage.getParameter().remove(SmsMsgEventPublish.TEMPLATE_CODE);
|
||||
smsMessage.getParameter().remove(SmsMsgEventPublish.CONTENT);
|
||||
// 短信模版参数
|
||||
smsParam.setParameters(smsMessage.getParameter());
|
||||
//@formatter:on
|
||||
SmsResponse send = smsProviderSend.send(smsParam);
|
||||
// 保存发送记录
|
||||
if (!Objects.isNull(send)) {
|
||||
//@formatter:off
|
||||
SmsSendRecordEntity record = new SmsSendRecordEntity()
|
||||
.setContent(content)
|
||||
.setResult(send.getMessage())
|
||||
.setSuccess(send.getSuccess())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setProvider(send.getProvider())
|
||||
.setCategory(MessageCategory.CODE)
|
||||
.setType(smsMessage.getType())
|
||||
.setPhone(smsParam.getPhone());
|
||||
record.setRemark(JSON.toJSONString(send));
|
||||
if (!send.getSuccess()) {
|
||||
log.error("发送短信失败: params: {}, response: {}", smsParam, send);
|
||||
}
|
||||
//@formatter:on
|
||||
smsSendLogRepository.save(record);
|
||||
} else {
|
||||
log.error("发送短信失败,返回值为空: params: {}, ", smsParam);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送短信消息异常 params:{}, error: {}", smsParam, e.getMessage());
|
||||
throw new SmsMessageSendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件通知
|
||||
*
|
||||
* @param mailMessage {@link MailMessage}
|
||||
*/
|
||||
private void sendMail(@NonNull MailMessage mailMessage) {
|
||||
if (mailProviderSend instanceof MailNoneProviderSend) {
|
||||
throw new MailMessageSendException("暂未配置邮件服务");
|
||||
}
|
||||
// 邮件通知类型
|
||||
MailType type = mailMessage.getType();
|
||||
String content = htmlUnescape(MailUtils.readEmailContent(type.getContent()));
|
||||
String subject = type.getSubject();
|
||||
String sender = type.getSender();
|
||||
try {
|
||||
Map<String, Object> parameter = mailMessage.getParameter();
|
||||
// 判断是否存在自定义
|
||||
MailTemplateEntity templateEntity = (MailTemplateEntity) redisTemplate.opsForValue()
|
||||
.get(MessageSettingConstants.SETTING_EMAIL_TEMPLATE_CACHE_NAME + COLON
|
||||
+ type.getCode());
|
||||
if (!Objects.isNull(templateEntity)) {
|
||||
content = htmlUnescape(templateEntity.getContent());
|
||||
subject = templateEntity.getSubject();
|
||||
sender = templateEntity.getSender();
|
||||
}
|
||||
// 主题
|
||||
StringWriter themeStringWriter = new StringWriter();
|
||||
Template themeTpl = createTemplate(type.getCode() + "-theme", subject);
|
||||
themeTpl.process(parameter, themeStringWriter);
|
||||
// 测试邮件
|
||||
if (parameter.containsKey(MsgVariable.TEST)) {
|
||||
String test = parameter.get(MsgVariable.TEST).toString();
|
||||
themeStringWriter.append(test);
|
||||
}
|
||||
// 内容
|
||||
StringWriter contentStringWriter = new StringWriter();
|
||||
Template contentTpl = createTemplate(type.getCode() + "-content", content);
|
||||
contentTpl.process(parameter, contentStringWriter);
|
||||
// 发送邮件
|
||||
//@formatter:off
|
||||
SendMailRequest params = new SendMailRequest()
|
||||
// 发送人
|
||||
.setSender(sender)
|
||||
// 接收人
|
||||
.setReceiver(mailMessage.getReceiver())
|
||||
// 主题
|
||||
.setSubject(themeStringWriter.toString())
|
||||
// 内容
|
||||
.setBody(contentStringWriter.toString());
|
||||
//@formatter:on
|
||||
// 发送邮件
|
||||
mailProviderSend.sendMailHtml(params);
|
||||
// 保存发送记录
|
||||
MailSendRecordEntity record = new MailSendRecordEntity();
|
||||
//@formatter:off
|
||||
record.setSender(mailMessage.getReceiver())
|
||||
.setSubject(params.getSubject())
|
||||
.setContent(params.getBody())
|
||||
.setProvider(mailProviderSend.getProvider())
|
||||
.setType(mailMessage.getType())
|
||||
.setReceiver(mailMessage.getReceiver())
|
||||
.setSendTime(LocalDateTime.now())
|
||||
.setSuccess(true);
|
||||
//@formatter:on
|
||||
mailSendRecordRepository.save(record);
|
||||
} catch (Exception e) {
|
||||
log.error("邮件信息发送失败: {}", e.getMessage());
|
||||
throw new MailMessageSendException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模板
|
||||
*
|
||||
* @param name {@link String} 模板名称
|
||||
* @param templateContent {@link String} 模板内容
|
||||
* @return {@link Template}
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private Template createTemplate(String name, String templateContent) throws IOException {
|
||||
Template template = freeMarkerConfiguration.getTemplate(name, null, null, UTF_8, true,
|
||||
true);
|
||||
if (template != null) {
|
||||
return template;
|
||||
}
|
||||
// 以下操作不是线程安全,要加上同步
|
||||
synchronized (this) {
|
||||
// 获取模板加载器
|
||||
TemplateLoader templateLoader = freeMarkerConfiguration.getTemplateLoader();
|
||||
if (templateLoader instanceof StringTemplateLoader) {
|
||||
// 如果加载器已经是字符串加载器,则在原来的加载器上put一个新的模板
|
||||
((StringTemplateLoader) templateLoader).putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(templateLoader);
|
||||
} else {
|
||||
// 如果原来的模板加载器不是字符串的(默认是文件加载器),则新建
|
||||
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
|
||||
stringTemplateLoader.putTemplate(name, templateContent);
|
||||
freeMarkerConfiguration.setTemplateLoader(stringTemplateLoader);
|
||||
}
|
||||
// 这里要清一下缓存,不然下面可能会获取不到模板
|
||||
freeMarkerConfiguration.clearTemplateCache();
|
||||
return freeMarkerConfiguration.getTemplate(name, UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MailSend
|
||||
*/
|
||||
private final MailProviderSend mailProviderSend;
|
||||
|
||||
/**
|
||||
* MailSendRecordRepository
|
||||
*/
|
||||
private final MailSendRecordRepository mailSendRecordRepository;
|
||||
|
||||
/**
|
||||
* redis
|
||||
*/
|
||||
private final RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
private final Configuration freeMarkerConfiguration;
|
||||
|
||||
/**
|
||||
* SmsSend
|
||||
*/
|
||||
private final SmsProviderSend smsProviderSend;
|
||||
|
||||
/**
|
||||
* SmsSendRecordRepository
|
||||
*/
|
||||
private final SmsSendRecordRepository smsSendLogRepository;
|
||||
|
||||
public NoticeMessageListener(MailProviderSend mailProviderSend,
|
||||
MailSendRecordRepository mailSendRecordRepository,
|
||||
RedisTemplate<Object, Object> redisTemplate,
|
||||
SmsProviderSend smsProviderSend,
|
||||
SmsSendRecordRepository smsSendLogRepository) {
|
||||
this.mailProviderSend = mailProviderSend;
|
||||
this.mailSendRecordRepository = mailSendRecordRepository;
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.smsProviderSend = smsProviderSend;
|
||||
this.smsSendLogRepository = smsSendLogRepository;
|
||||
this.freeMarkerConfiguration = new Configuration(Configuration.VERSION_2_3_31);
|
||||
//设置模板加载文件夹
|
||||
this.freeMarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/mail/content");
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* eiam-core - Employee Identity and Access Management
|
||||
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package cn.topiam.employee.core.mq;
|
||||
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.TopicExchange;
|
||||
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.RabbitConverterFuture;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import cn.topiam.employee.common.message.enums.MessageType;
|
||||
import cn.topiam.employee.support.constant.EiamConstants;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* MQ消息发送
|
||||
*
|
||||
* @author TopIAM
|
||||
* Created by support@topiam.cn on 2023/6/15 23:12
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NoticeMessagePublisher extends AbstractMessagePublisher {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
TopicExchange topicExchange = new TopicExchange(NOTICE);
|
||||
Queue smsQueue = new Queue(NOTICE_SMS, true);
|
||||
Queue mailQueue = new Queue(NOTICE_MAIL, true);
|
||||
amqpAdmin.declareExchange(topicExchange);
|
||||
amqpAdmin.declareQueue(smsQueue);
|
||||
amqpAdmin.declareQueue(mailQueue);
|
||||
amqpAdmin.declareBinding(BindingBuilder.bind(smsQueue).to(topicExchange).with(NOTICE_SMS));
|
||||
amqpAdmin
|
||||
.declareBinding(BindingBuilder.bind(mailQueue).to(topicExchange).with(NOTICE_MAIL));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param asyncRabbitTemplate {@link AsyncRabbitTemplate}
|
||||
* @param amqpAdmin {@link AmqpAdmin}
|
||||
*/
|
||||
public NoticeMessagePublisher(AsyncRabbitTemplate asyncRabbitTemplate, AmqpAdmin amqpAdmin) {
|
||||
super(asyncRabbitTemplate, amqpAdmin);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送异步短信邮件通知消息
|
||||
*
|
||||
* @param type {@link MessageType}
|
||||
* @param message {@link String}
|
||||
*/
|
||||
public void sendNotice(MessageType type, String message) {
|
||||
log.info("发送[{}]通知消息, message:[{}]", type.getDesc(), message);
|
||||
String routingKey = NOTICE + EiamConstants.POINT + type.getCode().toLowerCase();
|
||||
RabbitConverterFuture<Object> future = sendMessage(NOTICE, routingKey, message);
|
||||
future.whenComplete((result, ex) -> {
|
||||
if (ex == null) {
|
||||
log.info("发送[{}]通知消息成功, message:[{}],处理结果为:[{}]", type.getDesc(), message, result);
|
||||
} else {
|
||||
log.info("发送[{}]通知消息异常,message:[{}],失败原因:[{}], ", type.getDesc(), message,
|
||||
ex.getMessage(), ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue