消息发送优化

pull/63/MERGE
awenes 2023-10-02 15:31:56 +08:00
parent 1b9f750450
commit 73c940111d
8 changed files with 433 additions and 447 deletions

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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);
}
});
}
}