小程序云开发,客户下单给老板娘发提醒邮件

737 阅读3分钟

前言

最近帮朋友做了一个点单的微信小程序,使用了云开发,还是挺香的。

客户点单需要提醒老板娘,想到了两种方案:

  • 短信
  • 邮箱

最后选择了邮箱,免费的谁不爱呢。

大致流程是客户提交订单的逻辑在云函数中处理,提交订单时将订单信息发送给老板娘的邮箱。

先看下最终效果:

image.png

image.png

准备

首先需要准备一个邮箱,开通IMAP/SMTP服务,获得授权码。此处以QQ邮箱为例:

image.png

image.png

代码

发送邮件云函数

发送邮件需要用到nodemailer 库。

创建一个云函数 sendmail:

image.png

安装 nodemailer:

在 package.json 中添加 nodemailer 依赖,本地调试或部署时可自动安装。 也可进入到 sendmail 根目录,执行 npm install nodemailyarn add nodemail

{
  "name": "sendmail",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "wx-server-sdk": "~2.5.3",
    "nodemailer": "^6.6.3"
  }
}

sendmail 云函数代码

const cloud = require('wx-server-sdk')
const nodemailer = require('nodemailer')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

/*
message: 调用云函数时传入的数据,此处传入的是邮件信息
{ 
  from: "sender@server.com", // 发件人邮箱
  to: "receiver@sender.com", // 收件人邮箱
  subject: "邮件的标题",
  text: "邮件纯文字内容",
  html: "<p>可以发送 HTML</p>"
}
*/
exports.main = async (message, context) => {
  const sendEmail = nodemailer.createTransport({
    host: 'smtp.qq.com', // SMTP 服务器地址,此处是 QQ邮箱
    port: 465, // 端口号,通常为465,587,994,不同的邮件客户端端口号可能不一样
    secure: true, // 如果端口是465,就为true;如果是其它就填false
    auth: {
      user: 'xxx@qq.com', // 邮箱账号,填写已开启SMTP服务的邮箱地址即可
      pass: '此处填写之前取得的授权码' // 邮箱密码,不同的邮件系统可能机制不一样,QQ 邮箱和网易邮箱为邮箱授权码
    }
  })
  
  const res = await sendEmail.sendMail(message)
  return res
}

处理订单云函数

以下是订单的字段结构:

{
  "_id": "2d44d6c2610bc57902d23b2771c2861b",
  "address": "成都市春熙路边",
  "consignee": "李白",
  "datetime": 1628161200000,
  "phone": "18911112222",
  "products": [
    {
      "_id": "2d44d6c26100ff580072a06244a35695",
      "count": 1,
      "img": "cloud://yulu-z3s06.7975-yulu-z3s06-1301369817/cloudbase-cms/upload/2021-08-02/fgfbz3qiw53xggf6hq4ypbs8b515v6py_.jpg",
      "price": 166,
      "selectedSku": {
        "price": 166,
        "spec": [
          "六寸",
          "淡奶油"
        ]
      },
      "title": "韩式简约风"
    },
  ],
  "remark": "",
  "totalCount": 2,
  "totalPrice": 292,
  "status": "制作中",
  "_createTime": 1628161407261
}

直接上代码:

const cloud = require('wx-server-sdk')
const dayjs = require('dayjs')

// 初始化 cloud
cloud.init({
  // API 调用都保持和云函数当前所在环境一致
  env: cloud.DYNAMIC_CURRENT_ENV
})

exports.main = async (order, context) => {
  try {
    const db = cloud.database()
    const _ = db.command

    // 购买的商品销量增加
    order.products.forEach(({ _id, count }) => {
      db.collection('product')
        .doc(_id)
        .update({
          data: {
            sales: _.inc(count)
          }
        })
    })

    // 向数据库添加订单
    const data = await db.collection('order').add({
      data: {
        ...order,
        status: '制作中',
        _createTime: new Date().getTime()
      }
    })
    
    // 订单创建成功发邮件
    if (data._id) {
      // 捕获错误,避免发邮件出错影响导致前端提交订单时意外报错
      try {
        // 邮件内容
        const message = {
          from: 'xxxx@qq.com',
          to: 'xxx@qq.com',
          subject: '老板娘,来新的订单啦!',
          text: '新订单',
          html: createHtml(order)
        }
        
        // 云函数中可以调用其它云函数,此处调用之前创建的 sendmail
        cloud.callFunction({ name: 'sendmail', data: message })
      } catch (error) {}

      return data
    }
    throw new Error()
  } catch (error) {
    return {
      code: -1,
      msg: '提交失败,请稍后重试'
    }
  }
}

/**
  创建订单信息邮件的 HTML,可以编写自己喜欢的样式
*/
function createHtml ({
  consignee,
  phone,
  address,
  datetime,
  remark,
  products,
  totalPrice
} = {}) {
  return `
  <div style="display: flex; justify-content: center; align-items: center; padding: 40px 0;">
    <div
      style="padding: 20px; color: #333333; background-color: #fff; border-radius: 10px; box-shadow: 0 0 10px rgba(128, 128, 128, 0.3); letter-spacing: 1px;"
    >
      <div style="line-height: 1.6;">
        <div>联系人:${consignee}</div>
        <div>电话:${phone}</div>
        <div>地址:${address}</div>
        <div>送达时间:${dayjs(datetime).format('YYYY-MM-DD HH:mm')}</div>
      </div>
      <div
        style="margin-top: 15px; border-top: 2px dashed #e9e9e9; border-bottom: 2px dashed #e9e9e9;"
      >
        ${products
          .map(({ title, count, price, selectedSku }) => {
            return `<div style="display: flex; padding: 10px 0;">
              <div style="flex: 1;">${title}${
              selectedSku ? '(' + selectedSku.spec.join('+') + ')' : ''
            }</div>
              <div style="width: 60px; text-align: right;">×${count}</div>
              <div style="width: 60px; color: red; text-align: right;">¥${price}</div>
            </div>`
          })
          .join('')}
      </div>
      <div
        style="display: flex; justify-content: space-between; margin-top: 15px;"
      >
        <div>总金额</div>
        <div style="color: red;">¥${totalPrice}</div>
      </div>
      <div style="margin-top: 10px; line-height: 1.6;">备注:${remark}</div>
    </div>
  </div>`
}

部署

可现在本地调试后再上传部署。

image.png

最后

有需要的小伙伴可以直接复制代码使用。

感谢观看,不足之处或有疑问欢迎评论区讨论。