一二三应用开发平台——能力扩展:发送邮件

背景

开发平台具备最简内核能力后,需要集成一些第三方功能组件进行能力扩展,例如邮件发送、任务调度等。

邮件通知功能在业务系统中扮演着至关重要的角色,其用途广泛,不仅提高了工作效率,还增强了通信的便捷性和及时性。以下是邮件通知功能的几种常见用途:

  1. 提醒和警报
    • 系统可以通过邮件提醒用户即将到期或变更的任务、会议、账单支付等。
    • 向客户提供订单确认、发货通知、退货状态更新等信息。
    • 提供客户反馈和投诉的自动回复,提升客户满意度。
    • 在出现紧急情况或系统异常时,自动发送警报邮件给管理员或相关人员。
  2. 发送报表和报告
    • 定期发送业务报表、销售报告、财务报告等,帮助管理层和决策者及时了解业务状况。
    • 向项目团队成员发送项目进度报告,确保信息同步和透明。

邮件通知是业务系统的通知机制的主要方式之一,相比短信通知,特点是无额外费用,通知内容更丰富,可使用html,可携带附件。

今天我们就来说下如何通过集成功能组件,来实现邮件发送能力。

功能组件

在Java中,用于邮件发送的功能组件主要可以分为两大类:JavaMail API(现在称为 Jakarta Mail)和第三方库。以下是一些常用的组件和库:

  1. JavaMail API / Jakarta Mail
    • 这是Sun Microsystems提供的官方API,用于发送邮件。它是一个功能丰富、灵活的API,支持SMTP、POP3和IMAP协议。
    • 网址:Jakarta Mail
  2. Apache Commons Email
    • Apache Commons Email库简化了JavaMail的使用,提供了一些易于使用的高层API。
    • 网址:Apache Commons Email
  3. Spring Framework
    • Spring框架提供了对JavaMail的支持,通过Spring的MailSender接口和相关类简化邮件发送。
    • 网址:Spring Framework
  4. MailKit
    • MailKit是一个基于JavaMail API的邮件处理库,用于替代JavaMail API。
    • 网址:MailKit

邮件发送是一个常见的典型通用需求,因此市面上也有众多的组件来支撑这块功能,上面列的仅是主流的几个。

平台基于SpringBoot脚手架,因此优先选择了spring-boot-starter-mail功能组件,其内部集成的实际是Jakarta Mail功能组件。

系统集成

创建模块

开发平台下单设一个模块,专门用于邮件发送功能的集成,命名为platform-boot-starter-mail。

模块内部结构如下:

添加依赖

在模块的pom文件中,增加组件依赖,如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

组件配置

发送邮件需要配置邮件服务器的信息,在项目的配置文件中,在spring的节点下,新增mail节点,以使用网易的126邮箱为例,配置邮件服务器的主机地址、账号和密码三项信息。

自定义配置

上面环节是配置邮件服务器的信息,实际上,收件人收取邮件时显示的发件人,可以一定程度上自行制定,例如网易邮箱可以为126邮箱绑定一个手机号,并可以开启类似别名功能,手机号+@126.com可以作为邮箱地址来发送或接收邮件。

平台自定义一个配置参数,来灵活设置发件人邮箱,如下:

package tech.abc.platform.mail.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


/**
 * 对象存储配置文件
 *
 * @author wqliu
 * @date 2020-04-05 11:19
 */
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "platform-config.mail")
public class MailConfig {

    /**
     * 发送方地址
     */
    private String senderAddress = "";

}

定义实体

将邮件的核心要素梳理出来,如发件人、收件人、抄送、密送、主题、内容、附件等,定义一个邮件类来对应,如下:

package tech.abc.platform.mail.entity;

import lombok.Data;

import java.util.List;

/**
 * 邮件 实体对象
 *
 * @author wqliu
 * @date 2023-05-25
 */
@Data
public class Mail {

    /**
     * 发件人
     */
    private String from;
    /**
     * 收件人
     */
    private String to;
    /**
     * 抄送人
     */
    private String cc;
    /**
     * 密送人
     */
    private String bcc;

    /**
     * 主题
     */
    private String subject;
    /**
     * 内容
     */
    private String text;

    /**
     * 附件路径
     */
    private List<String> attachmentList;


}

定义服务接口

邮件发送的服务接口,平台规划和设计了两类,一类是发送简单的纯文本信息,另一类则是发送可以使用html和携带附件的富文本信息。

同时为了便于调用方使用,设置了重载方法,既可以构建一个邮件对象传入,也可以直接传入收件人、主题、内容三要素。

完整源码如下:

package tech.abc.platform.mail.service;

import tech.abc.platform.mail.entity.Mail;


/**
 * 邮件服务
 *
 * @author wqliu
 * @date 2023-05-25
 */
public interface MailService {

    /**
     * 发送纯文本邮件,传入参数
     *
     * @param to      收件人
     * @param subject 主题
     * @param content 内容
     */
    void sendTextMail(String to, String subject, String content);

    /**
     * 发送纯文本邮件,传入对象,不可携带附件
     *
     * @param mail
     */
    void sendTextMail(Mail mail);


    /**
     * 发送Html邮件,传入参数
     *
     * @param to      收件人
     * @param subject 主题
     * @param content 内容
     */
    void sendHtmlMail(String to, String subject, String content);


    /**
     * 发送Html邮件,传入对象,可携带附件
     *
     * @param mail
     */
    void sendHtmlMail(Mail mail);
}

服务实现

服务实现类比较简单,按照邮件功能组件的要求,组织数据调用即可,如下:

package tech.abc.platform.mail.service.impl;

import ma.glasnost.orika.MapperFacade;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import tech.abc.platform.common.exception.CustomException;
import tech.abc.platform.mail.config.MailConfig;
import tech.abc.platform.mail.entity.Mail;
import tech.abc.platform.mail.exception.MailExceptionEnum;
import tech.abc.platform.mail.service.MailService;

import javax.activation.FileDataSource;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.util.List;


/**
 * 使用spring mail组件发送邮件的服务实现类
 *
 * @author wqliu
 * @date 2023-05-25
 */
@Service
public class SpringMailServiceImpl implements MailService {

    private static final String COMMA = ",";
    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private MailConfig mailConfig;

    @Autowired
    private MapperFacade mapperFacade;

    @Override
    public void sendTextMail(String to, String subject, String content) {
        Mail mail = new Mail();
        mail.setTo(to);
        mail.setSubject(subject);
        mail.setText(content);
        sendTextMail(mail);
    }

    @Override
    public void sendTextMail(Mail mail) {
        try {
            // 设置对象
            SimpleMailMessage message = new SimpleMailMessage();
            BeanUtils.copyProperties(mail, message);
            // 发件人由后端指定
            message.setFrom(mailConfig.getSenderAddress());
            // 处理收件人
            String[] to = getArray(mail.getTo());
            if (to != null) {
                message.setTo(to);
            }
            // 处理抄送人
            String cc = mail.getCc();
            if (cc != null) {
                message.setCc(cc);
            }
            // 处理密送人
            String bcc = mail.getBcc();
            if (bcc != null) {
                message.setBcc(bcc);
            }
            // 发送邮件
            mailSender.send(message);
        } catch (Exception e) {
            throw new CustomException(MailExceptionEnum.SEND_ERROR, e.getMessage());
        }
    }


    @Override
    public void sendHtmlMail(String to, String subject, String content) {

        Mail mail = new Mail();
        mail.setTo(to);
        mail.setSubject(subject);
        mail.setText(content);
        sendHtmlMail(mail);
    }

    @Override
    public void sendHtmlMail(Mail mail) {

        try {
            // 设置对象
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper messageHelper = new MimeMessageHelper(message, true);

            // 发件人由后端指定
            messageHelper.setFrom(mailConfig.getSenderAddress());
            // 处理收件人
            String[] to = getArray(mail.getTo());
            if (to != null) {
                messageHelper.setTo(to);
            }
            // 处理抄送人
            String cc = mail.getCc();
            if (cc != null) {
                messageHelper.setCc(cc);
            }
            // 处理密送人
            String bcc = mail.getBcc();
            if (bcc != null) {
                messageHelper.setBcc(bcc);
            }
            messageHelper.setSubject(mail.getSubject());
            messageHelper.setText(mail.getText(), true);

            // 附件处理
            List<String> attachmentList = mail.getAttachmentList();
            if (CollectionUtils.isNotEmpty(attachmentList)) {
                for (int i = 0; i < attachmentList.size(); i++) {
                    String filePath = attachmentList.get(i);
                    String filename = FilenameUtils.getName(filePath);
                    messageHelper.addAttachment(MimeUtility.encodeWord(filename), new FileDataSource(filePath));
                }
            }
            // 发送邮件
            mailSender.send(message);
        } catch (Exception e) {
            throw new CustomException(MailExceptionEnum.SEND_ERROR, e.getMessage());
        }
    }


    /**
     * 将字符串转换为数组
     *
     * @param address
     * @return
     */
    private String[] getArray(String address) {
        if (StringUtils.isNotBlank(address)) {
            if (StringUtils.contains(address, COMMA)) {
                return address.split(COMMA);
            } else {
                return new String[]{address};
            }

        }
        return null;
    }
}

调用示例

下面是发送邮件服务的调用示例,对应的应用场景是当用户忘记密码时,输入注册时填写的邮箱地址,由系统发送一封重置密码的邮件,源码如下:

private void sendResetPasswordEmail(String email, String account) {
    // 生成唯一性编码
    String code = UUID.randomUUID().toString();
    // 存入redis,24小时后失效
    cacheUtil.set(code, account, 24, TimeUnit.HOURS);


    // 生成内容
    String systemUrl = systemConfig.getSystemUrl();
    String resetUrl = systemUrl + "/#/selfResetPassword?code=" + code;
    String content = MessageFormat.format("重设【遇见】应用密码,请点击链接或将其复制到浏览器地址栏:<a href=\"{0}\">{0}</a>", resetUrl);


    // 使用线程异步发送邮件
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            mailService.sendHtmlMail(email, "重设【遇见】应用密码", content);
        }
    };
    // noinspection AlibabaAvoidManuallyCreateThread
    Thread thread = new Thread(runnable);
    // 启动
    thread.start();

}

此外,这里提一下,这个场景比较简单,直接使用字符串拼接的邮件正文内容。如对邮件正文有复杂的格式,并且需要灵活修改,可以使用平台业务支撑模块下的内容模板功能,使用富文本编辑器设置好格式、内容和变量,读取业务数据后,替换变量,再调用邮件发送功能。

开源平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

17本资源是语音合成(TTS,Text-to-Speech)技术的专项实战课程教案,系统性地讲解了如何利用百度AI开放平台的语音合成服务,通过Python SDK方式将文本信息转换为自然、流畅的语音文件(如MP3格式)。内容从语音合成技术的核心原理(机械/电子方法产生人造语音)及其三大关键技术流程(语言处理->音律处理->声学处理)引入,深入浅出地介绍了主流合成技术(如LPC、PSOLA、LMA)的优缺点与适用场景。课程核心是百度语音合成服务的开通与配置,复用语音技术应用的调用凭证(AppID, API Key, Secret Key)。重点详细演示了Python SDK的调用模式:从安装配置aip库、初始化AipSpeech客户端,到使用核心方法client.synthesis()进行语音合成。教案深入讲解了synthesis方法的关键参数配置:待合成文本(<1024字节)、语言类型('zh'中文)、语速(1为默认)、音调、音量('vol': 5)等,并指导学生如何将返回的二进制语音数据正确写入本地文件(如audio.mp3),完成从文本到语音的完整生成流程。 适用人群: • 中小学信息技术教师、STEM教育从业者:作为引入智能语音交互与多媒体内容生成课程的备课参考与教学素材,提供从理论到合成的完整项目实现方案。 • 对AI语音应用开发感兴趣,希望掌握将文本信息转化为语音输出能力的青少年学员(建议初中及以上):作为项目实践指导,帮助理解文语转换的技术流程与参数控制。 • 所有希望快速为应用(如智能朗读、语音提醒、有声内容生成)添加语音输出功能的开发者、内容创作者:作为一份聚焦语音合成SDK集成与调用的实战参考手册。 使用场景及目标: • 教学场景:用于120分钟的线下或线上编程实践课堂。教师可依据教案设定的环节、时间分配(如技术原理15-25分钟,服务配置10-1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行者无疆1982

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值