这两天给博客加上了邮件提醒功能,之前用户评论留言之后是没有任何反馈的,体验很不好,用户粘度也会大大降低。现在提醒功能已经上线了,大家都能及时收到回复了!下面分享一下邮件提醒功能在 nestjs 中的实现。
我使用的是大家比较常用的 QQ 邮箱,而要使用 QQ 邮箱的话,需要先登录 QQ 邮箱进行设置,获取到授权码才行,具体操作如下
1- 登录后点击【设置】
2- 在设置页面选择【账号】
3- 在账号页面找到【POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务】,然后【开启服务】
4- 点击【开启服务】后会要求绑定手机号,进行验证。通过后就会给你一个授权码,这个授权码在后续代码中会使用到。不需要记住,通过【管理服务】进入可以查看到已经生成的授权码。
邮箱配置好后,我们需要安装所需的依赖包,总共四个
dependencies
1- @nestjs-modules/mailer
2- nodemailer
3- nodemailer-smtp-transport
devDependencies
4- @types/nodemailer-smtp-transport
由于使用了 typescript 所以需要安装类型 @types/nodemailer-smtp-transport ,不然会报类型错误。每个人使用的包管理器可能不一样,大家根据自己的情况执行安装就行。我用的是 pnpm
安装生产依赖包
pnpm add @nestjs-modules/mailer nodemailer nodemailer-smtp-transport
安装开发依赖包
pnpm add -D @types/nodemailer-smtp-transport
准备工作都做完了,现在开始写代码了,找到项目中的 app.module.ts 文件,添加如下代码,进行全局配置
// 引入包
import { MailerModule } from '@nestjs-modules/mailer'
import * as SMTPTransport from 'nodemailer-smtp-transport'
@Module({
imports: [
MailerModule.forRoot({
transport: SMTPTransport({
host: 'smtp.qq.com',
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: '此处填写你的邮箱',
pass: '此处填写上文中拿到的授权码', // 建议放在配置文件,不要直接硬编码写在程序里
}
})
}),
]
})
全局配置完成后,就可以在 service 中使用了,在你需要发送邮件的地方加上逻辑即可。比如在用户发表评论后,邮件通知我去审核。
comment.service.ts
// 引入邮件Service
import { MailerService } from '@nestjs-modules/mailer'
// 注入
@Injectable()
export class CommentService {
constructor(
private readonly mailerService: MailerService
) {}
async create(createCommentDto: CreateCommentDto) {
const result = await this.prisma.comment.create({
data: createCommentDto
})
// 发送邮件
this.mailerService.sendMail({
from: '此处填写发送人',
to: '此处填写你的收件箱',
subject: '博客有新的评论待处理', // 邮件主题
text: `${createCommentDto.nickName}: ${createCommentDto.content}`, // 邮件内容
})
return result
}
}
需要注意的点:
1- from 必须配置,因为 QQ 邮箱 SMTP 服务对于 from 有严格验证,不配置就会报错。这里我踩了大坑,找了好久的报错原因,因为报错信息并不会提示你需要配置 from,只告诉你参数有误。
2- from 格式与你 QQ 邮箱的名称保持一致,比如我的 QQ 邮箱名称如下图,那么这里我就填写 from: '小贰<aaron.yyf@qq.com>'
邮件内容美化,指的就是让你发出去的邮件格式工整,排版美观,可读性更好。其意义就在于给用户一个良好的体验,能更直观的抓住内容重点。邮件内容除了支持 text ,它还支持 html ,而 html 里面是可以写 css 样式的,这样我们就可以通过 html + css 来控制邮件的排版。
上面的示例:发送邮件提醒我审核用户评论。它是没做美化的(直接用的 text),因为是我自己看,所以无所谓,但是发送给用户的邮件是有必要做美化的。先贴一张美化后的图
怎么样,用户收到这样的邮件是不是很清爽,阅读起来是不是更舒服,比一堆文字放在那里要强上不少吧。需要注意的是,在邮箱里书写 html 和在网页里还不太一样,主要是兼容性问题。在邮箱里通常采用 table 布局,而且样式需要写成内连样式。下面是一个具体的示例:
// 发送邮件
this.mailerService.sendMail({
from: process.env.EMAIL_FROM, // 从配置文件读取 from
to: parent.email,
subject: `您在文章《${article.title}》的评论收到了新的回复`,
html:
`<div style="background-color: #f5f5f5;border-radius: 5px;max-width: 580px;padding: 10px;">
<table style="width: 100%;border-collapse: collapse;background-color: #fff;">
<tr>
<td style="padding:40px 0;">
<center>
<p style="font-size: 18px;font-weight: bold;">鄢云峰的个人网站</p>
</center>
</td>
</tr>
<tr>
<td style="padding: 0 20px 40px 20px; font-size: 14px;">
<p style="color: #666;">您好,${parent.nickName}!你于 ${Utils.dateFormat(parent.createTime)} 在文章《${article.title}》发表的评论:</p>
<p style="color: #333;background-color: #eee;padding:20px;border-radius: 4px;">${parent.content}</p>
</td>
</tr>
<tr>
<td style="padding: 0 20px 40px 20px; font-size: 14px;">
<p style="color: #666;">收到了来自于 ${createCommentDto.nickName}(${Utils.dateFormat(result.createTime)}) 的回复:</p>
<p style="color: #333;background-color: #eee;padding:20px;border-radius: 4px;">${createCommentDto.content}</p>
</td>
</tr>
<tr>
<td style="padding: 40px 20px; font-size: 14px; color: #666;">
<span>查看详情:</span>
<a href="https://yanyunfeng.com/article/${article.id}">https://yanyunfeng.com/article/${article.id}</a>
</td>
</tr>
<tr>
<td style="background-color: #f1f1f1; padding: 20px 0;">
<center>
<div style="max-width: 600px;">
<p style="font-size: 14px; color: #999;">此邮件为系统自动发送,请勿直接回复</p>
</div>
</center>
</td>
</tr>
</table>
</div>`
})