依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
sql表
DROP TABLE IF EXISTS `t_mail_log`;
CREATE TABLE `t_mail_log` (
`msgId` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '消息id',
`eid` INT(11) DEFAULT NULL COMMENT '接收员工id',
`status` INT(1) DEFAULT NULL COMMENT '状态(0:消息投递中 1:投递成功 2:投递失败)',
`routeKey` VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '路由键',
`exchange` VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '交换机',
`count` INT(1) DEFAULT NULL COMMENT '重试次数',
`tryTime` DATETIME DEFAULT NULL COMMENT '重试时间',
`createTime` DATETIME DEFAULT NULL COMMENT '创建时间',
`updateTime` DATETIME DEFAULT NULL COMMENT '更新时间',
UNIQUE KEY `msgId` (`msgId`) USING BTREE
) ENGINE=INNODB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
INSERT INTO `t_mail_log`(`msgId`,`eid`,`status`,`routeKey`,`exchange`,`count`,`tryTime`,`createTime`,`updateTime`) VALUES
('123',538,1,'mail.routing.key','mail.exchange',0,'2021-09-04 21:11:56','2021-09-04 21:10:56','2021-09-04 21:10:56');
application.yml配置文件
spring:
redis:
timeout: 10000ms
host: 106.14.223.42
port: 6379
database: 0
mail:
host: smtp.163.com
username: zsjia8@163.com
password: CVSXFAKKHTSAWCZY
default-encoding: utf-8
protocol: smtp
port: 25
rabbitmq:
username: admin
password: admin
host: 106.14.223.42
port: 5672
publisher-confirm-type: correlated
publisher-returns: true
listener:
simple:
acknowledge-mode: manual
thymeleaf:
cache: false
prefix: classpath:/templates/
suffix: .html
profiles:
active: pro
comment.avatar: /images/avatar.jpg
server:
port: 8099
mybatis-plus:
mapper-locations: classpath*:/mapper/*Mapper.xml
type-aliases-package: com.ljh.po
configuration:
map-underscore-to-camel-case: false
实体类MailLog
package com.ljh.po;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("t_mail_log")
@ApiModel(value="MailLog对象", description="")
public class MailLog implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "消息id")
private String msgId;
@ApiModelProperty(value = "接收员工id")
private Integer eid;
@ApiModelProperty(value = "状态(0:消息投递中 1:投递成功 2:投递失败)")
private Integer status;
@ApiModelProperty(value = "路由键")
private String routeKey;
@ApiModelProperty(value = "交换机")
private String exchange;
@ApiModelProperty(value = "重试次数")
private Integer count;
@ApiModelProperty(value = "重试时间")
private LocalDateTime tryTime;
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间")
private LocalDateTime updateTime;
}
公用类MailConstants
package com.ljh.po;
public class MailConstants {
public static final Integer DELIVERING = 0;
public static final Integer SUCCESS = 1;
public static final Integer FAILURE = 2;
public static final Integer MAX_TRY_COUNT = 3;
public static final Integer MSG_TIMEOUT = 1;
public static final String MAIL_QUEUE_NAME = "mail.queue";
public static final String MAIL_EXCHANGE_NAME = "mail.exchange";
public static final String MAIL_ROUTING_KEY_NAME = "mail.routing.key";
}
RabbitMq配置类RabbitMqConfig
@Configuration
public class RabbitMqConfig {
@Autowired
private CachingConnectionFactory cachingConnectionFactory;
@Autowired
private IMailLogService mailLogService;
private static final Logger LOGGER = LoggerFactory.getLogger(RabbitMqConfig.class);
@Bean
public RabbitTemplate rabbitTemplate(){
RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
rabbitTemplate.setConfirmCallback((correlationData,ack,cause)->{
String msgId = correlationData.getId();
System.out.println("msgId:"+msgId);
if (ack){
LOGGER.info("消息发送成功========》{}",msgId);
mailLogService.update(new UpdateWrapper<MailLog>().set("status",1).eq("msgId",msgId));
}
else {
LOGGER.info("消息发送失败==========>{}",msgId);
}
});
rabbitTemplate.setReturnCallback((msg,repCode,repText,exchange,routingkey)->{
LOGGER.info("{}============>消息发送到queue时失败"+msg.getBody());
});
return rabbitTemplate;
}
@Bean
public Queue queue(){
return new Queue(MailConstants.MAIL_QUEUE_NAME);
}
@Bean
public DirectExchange exchange(){
return new DirectExchange(MailConstants.MAIL_EXCHANGE_NAME);
}
@Bean
public Binding binding(){
return BindingBuilder.bind(queue()).to(exchange()).with(MailConstants.MAIL_ROUTING_KEY_NAME);
}
}
Service层、Mapper层代码
public interface IMailLogService extends IService<MailLog> {
}
@Service
public class MailLogServiceImpl extends ServiceImpl<MailLogMapper, MailLog> implements IMailLogService {
}
@Repository
@Mapper
public interface MailLogMapper extends BaseMapper<MailLog> {
}
controller层发送消息到rabbitmq
String msgId = UUID.randomUUID().toString();
MailLog mailLog = new MailLog();
mailLog.setMsgId(msgId);
mailLog.setEid(user1.getId().intValue());
mailLog.setStatus(MailConstants.DELIVERING);
mailLog.setRouteKey(MailConstants.MAIL_ROUTING_KEY_NAME);
mailLog.setExchange(MailConstants.MAIL_EXCHANGE_NAME);
mailLog.setCount(0);
mailLog.setTryTime(LocalDateTime.now().plusMinutes(MailConstants.MSG_TIMEOUT));
mailLog.setCreateTime(LocalDateTime.now());
mailLog.setUpdateTime(LocalDateTime.now());
mailLogMapper.insert(mailLog);
rabbitTemplate.convertAndSend(MailConstants.MAIL_EXCHANGE_NAME,MailConstants.MAIL_ROUTING_KEY_NAME
,user1,new CorrelationData(msgId));
消息接收,发送邮件MailReceiver
@Component
public class MailReceiver {
private static final Logger LOGGER = LoggerFactory.getLogger(MailReceiver.class);
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private MailProperties mailProperties;
@Autowired
private TemplateEngine templateEngine;
@Autowired
private RedisTemplate redisTemplate;
@RabbitListener(queues = MailConstants.MAIL_QUEUE_NAME)
public void handler(Message message, Channel channel) throws IOException {
MimeMessage msg = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(msg);
User user = (User) message.getPayload();
MessageHeaders headers = message.getHeaders();
Long tag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
String msgId = (String) headers.get("spring_returned_message_correlation");
HashOperations hashOperations = redisTemplate.opsForHash();
try {
if (hashOperations.entries("mail_log").containsKey(msgId)){
LOGGER.error("消息已经消费过了============》{}",msgId);
channel.basicAck(tag,false);
return;
}
helper.setFrom(mailProperties.getUsername());
helper.setTo(user.getEmail());
helper.setSentDate(new Date());
helper.setSubject("入职欢迎邮件");
Context context = new Context();
context.setVariable("name",user.getNickname());
String mail = templateEngine.process("mail", context);
helper.setText(mail,true);
javaMailSender.send(msg);
hashOperations.put("mail_log",msgId,"ok");
channel.basicAck(tag,false);
} catch (Exception e) {
channel.basicNack(tag,false,true);
LOGGER.error("邮件发送失败======={}",e.getMessage());
}
}
}
定时任务检查消息是否发送(记得在启动了添加@EnableScheduling)
@Component
public class MailTask {
@Autowired
private IMailLogService mailLogService;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private loginRepository loginRepository;
@Scheduled(cron = "0/10 * * * * ?")
public void mailtask(){
List<MailLog> list = mailLogService.list(new QueryWrapper<MailLog>()
.eq("status", 0)
.lt("tryTime", LocalDateTime.now()));
list.forEach(mailLog -> {
if (mailLog.getCount()>=3){
mailLogService.update(new UpdateWrapper<MailLog>()
.set("status",2)
.eq("msgId",mailLog.getMsgId()));
}
mailLogService.update(new UpdateWrapper<MailLog>().set("count",mailLog.getCount()+1)
.set("updateTime",LocalDateTime.now())
.set("tryTime",LocalDateTime.now().plusMinutes(MailConstants.MSG_TIMEOUT))
.eq("msgId",mailLog.getMsgId()));
User user = loginRepository.getOne(mailLog.getEid().longValue());
rabbitTemplate.convertAndSend(MailConstants.MAIL_EXCHANGE_NAME,MailConstants.MAIL_ROUTING_KEY_NAME,
user,new CorrelationData(mailLog.getMsgId()));
});
}
}
前端邮件模板mail.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.theymeleaf.org">
<head>
<meta charset="UTF-8">
<title>欢迎邮件</title>
</head>
<body>
欢迎 <span th:text="${name}"></span>,成功注册在线笔记系统
</body>
</html>