package cn.iocoder.yudao.module.system.service.mail; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Validator; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; import cn.iocoder.yudao.module.system.service.member.MemberService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.dromara.hutool.extra.mail.MailAccount; import org.dromara.hutool.extra.mail.MailUtil; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.io.File; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** * 邮箱发送 Service 实现类 * * @author wangjingyi * @since 2022-03-21 */ @Service @Validated @Slf4j public class MailSendServiceImpl implements MailSendService { @Resource private AdminUserService adminUserService; @Resource private MemberService memberService; @Resource private MailAccountService mailAccountService; @Resource private MailTemplateService mailTemplateService; @Resource private MailLogService mailLogService; @Resource private MailProducer mailProducer; @Override public Long sendSingleMail(Collection toMails, Collection ccMails, Collection bccMails, Long userId, Integer userType, String templateCode, Map templateParams, File... attachments) { // 1.1 校验邮箱模版是否合法 MailTemplateDO template = validateMailTemplate(templateCode); // 1.2 校验邮箱账号是否合法 MailAccountDO account = validateMailAccount(template.getAccountId()); // 1.3 校验邮件参数是否缺失 validateTemplateParams(template, templateParams); // 2. 组装邮箱 String userMail = getUserMail(userId, userType); Collection toMailSet = new LinkedHashSet<>(); Collection ccMailSet = new LinkedHashSet<>(); Collection bccMailSet = new LinkedHashSet<>(); if (Validator.isEmail(userMail)) { toMailSet.add(userMail); } if (CollUtil.isNotEmpty(toMails)) { toMails.stream().filter(Validator::isEmail).forEach(toMailSet::add); } if (CollUtil.isNotEmpty(ccMails)) { ccMails.stream().filter(Validator::isEmail).forEach(ccMailSet::add); } if (CollUtil.isNotEmpty(bccMails)) { bccMails.stream().filter(Validator::isEmail).forEach(bccMailSet::add); } if (CollUtil.isEmpty(toMailSet)) { throw exception(MAIL_SEND_MAIL_NOT_EXISTS); } // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams); String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams); Long sendLogId = mailLogService.createMailLog(userId, userType, toMailSet, ccMailSet, bccMailSet, account, template, content, templateParams, isSend); // 发送 MQ 消息,异步执行发送短信 if (isSend) { mailProducer.sendMailSendMessage(sendLogId, toMailSet, ccMailSet, bccMailSet, account.getId(), template.getNickname(), title, content, attachments); } return sendLogId; } private String getUserMail(Long userId, Integer userType) { if (userId == null || userType == null) { return null; } if (UserTypeEnum.ADMIN.getValue().equals(userType)) { AdminUserDO user = adminUserService.getUser(userId); if (user != null) { return user.getEmail(); } } if (UserTypeEnum.MEMBER.getValue().equals(userType)) { return memberService.getMemberUserEmail(userId); } return null; } @Override public void doSendMail(MailSendMessage message) { // 1. 创建发送账号 MailAccountDO account = validateMailAccount(message.getAccountId()); MailAccount mailAccount = buildMailAccount(account, message.getNickname()); // 2. 发送邮件 try { String messageId = MailUtil.send(mailAccount, message.getToMails(), message.getCcMails(), message.getBccMails(), message.getTitle(), message.getContent(), true, message.getAttachments()); // 3. 更新结果(成功) mailLogService.updateMailSendResult(message.getLogId(), messageId, null); } catch (Exception e) { // 3. 更新结果(异常) mailLogService.updateMailSendResult(message.getLogId(), null, e); } } private MailAccount buildMailAccount(MailAccountDO account, String nickname) { String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail(); return new MailAccount().setFrom(from).setAuth(true) .setUser(account.getUsername()).setPass(account.getPassword().toCharArray()) .setHost(account.getHost()).setPort(account.getPort()) .setSslEnable(account.getSslEnable()).setStarttlsEnable(account.getStarttlsEnable()); } @VisibleForTesting MailTemplateDO validateMailTemplate(String templateCode) { // 获得邮件模板。考虑到效率,从缓存中获取 MailTemplateDO template = mailTemplateService.getMailTemplateByCodeFromCache(templateCode); // 邮件模板不存在 if (template == null) { throw exception(MAIL_TEMPLATE_NOT_EXISTS); } return template; } @VisibleForTesting MailAccountDO validateMailAccount(Long accountId) { // 获得邮箱账号。考虑到效率,从缓存中获取 MailAccountDO account = mailAccountService.getMailAccountFromCache(accountId); // 邮箱账号不存在 if (account == null) { throw exception(MAIL_ACCOUNT_NOT_EXISTS); } return account; } /** * 校验邮件参数是否缺失 * * @param template 邮箱模板 * @param templateParams 参数列表 */ @VisibleForTesting void validateTemplateParams(MailTemplateDO template, Map templateParams) { template.getParams().forEach(key -> { Object value = templateParams.get(key); if (value == null) { throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key); } }); } }