Golang Wails构建简单应用(1)

安装

某天通过v2了解到了golang编写的redis客户端,感觉很奇妙,没想到可以这样构建应用,正好有简单需求,所以想起来这个,于是简单尝试了一下。

首先通过官网的指引,一步步的进行安装。前置依赖需要golang 和 Node,然后直接go install github.com/wailsapp/wails/v2/cmd/wails@latest安装就行

要注意golang的环境变量配置,不然可能找不到。

安装完成后,需要执行下 wails doctor,检查下相关的依赖和环境。

运行

使用命令wails init -n myproject -t vue-ts

也可以使用第三方模板进行初始化wails init -n <Your Appname> -t https://github.com/misitebao/wails-template-vue[@version]

如果需要调试的话,创建项目的时候可以加上wails init -n yourProject --ide goland

这里,我使用最简单的方式构建一下wails init -n MyJob -t vue-ts --ide goland

开发运行的话使用 wails dev

构建项目使用wails build

需求:群发邮箱

1 使用smtp发送邮件

2 使用imap保存邮件

3 根据文件读取发件人和附件

首先创建邮件,在app.go中创建函数

func createEmail(to, cc, bcc []string, attachmentPath string) []byte {
	var b bytes.Buffer
	writer := multipart.NewWriter(&b)
	smtpUser := "user@user.com"
	subject := GetFileName(attachmentPath)
	body := `<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>月度账单通知</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
        }
        .indent {
            text-indent: 2em; /* 缩进 2em */
        }
        .section {
            margin-bottom: 1em; /* 段落间距 */
        }
        .redfont {
            color: red;
        }
    </style>
</head>
<body>
<p>尊敬的合作伙伴:</p>
<p class="indent">大家好!</p>
<br>
<p class="indent">顺颂商祺。</p>
</body>
</html>
			`
	// 写入邮件头
	header := textproto.MIMEHeader{}
	header.Set("From", smtpUser)
	header.Set("To", strings.Join(to, ", "))
	if len(cc) > 0 {
		header.Set("Cc", strings.Join(cc, ", "))
	}
	header.Set("Subject", subject)
	header.Set("MIME-Version", "1.0")
	header.Set("Content-Type", fmt.Sprintf("multipart/mixed; boundary=%s", writer.Boundary()))

	for k, v := range header {
		fmt.Fprintf(&b, "%s: %s\r\n", k, v[0])
	}
	b.WriteString("\r\n")

	// 写入邮件正文
	part, _ := writer.CreatePart(textproto.MIMEHeader{"Content-Type": {"text/html"}})
	part.Write([]byte(body))

	// 添加附件
	attachment, err := os.ReadFile(attachmentPath)
	if err != nil {
		log.Fatal(err)
	}

	part, _ = writer.CreatePart(textproto.MIMEHeader{
		"Content-Type":              {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
		"Content-Transfer-Encoding": {"base64"},
		"Content-Disposition":       {"attachment; filename=\" " + subject + ".xlsx\""},
	})
	encoder := base64.NewEncoder(base64.StdEncoding, part)
	encoder.Write(attachment)
	encoder.Close()

	writer.Close()
	return b.Bytes()
}

因为需要支持 抄送和密送,所以传入的时候要加上header.Set("Cc", strings.Join(cc, ", "))

附件的Content-Type不确定的话,可以使用writer := multipart.NewWriter(body)来获取合适的类型

然后使用smtp发送邮件

func sendEmail(to, cc, bcc []string, emailContent []byte) error {
	// 配置 SMTP 服务器信息
	smtpHost := "smtp.xx.com"
	smtpPort := "465"
	smtpUsername := "user@user.com"
	smtpPassword := "xxxxxxxxxx"
	from := smtpUsername

	// 发送邮件
	// 设置 TLS 配置
	tlsConfig := &tls.Config{
		ServerName: smtpHost,
	}

	// 建立 TLS 连接
	conn, err := tls.Dial("tcp", smtpHost+":"+smtpPort, tlsConfig)
	if err != nil {
		return fmt.Errorf("failed to create TLS connection: %v", err)
	}

	// 创建 SMTP 客户端
	client, err := smtp.NewClient(conn, smtpHost)
	if err != nil {
		return fmt.Errorf("failed to create SMTP client: %v", err)
	}
	defer client.Close()

	// 认证
	auth := smtp.PlainAuth("", smtpUsername, smtpPassword, smtpHost)
	if err = client.Auth(auth); err != nil {
		return fmt.Errorf("failed to authenticate: %v", err)
	}

	// 设置发件人和收件人
	if err = client.Mail(from); err != nil {
		return fmt.Errorf("failed to set sender: %v", err)
	}

	// 设置所有收件人(包括 To、Cc 和 Bcc)
	recipients := append(append(to, cc...), bcc...)
	for _, recipient := range recipients {
		if err = client.Rcpt(recipient); err != nil {
			return fmt.Errorf("failed to add recipient %s: %v", recipient, err)
		}
	}

	// 发送邮件内容
	writer, err := client.Data()
	if err != nil {
		return fmt.Errorf("failed to create data writer: %v", err)
	}
	_, err = writer.Write(emailContent)
	if err != nil {
		return fmt.Errorf("failed to write email content: %v", err)
	}
	err = writer.Close()
	if err != nil {
		return fmt.Errorf("failed to close data writer: %v", err)
	}

	return client.Quit()
}

这里有一个问题,就是需要配置TLS,然后将上一步构建的邮件内容添加上就行

然后再说保存,如果需要在其他客户端查看自动发送的记录,就需要imap协议登录邮箱后,单独保存到指定的邮箱目录中

func saveToSentFolder(emailContent []byte) error {
	// 连接到 IMAP 服务器
	imapAddr := "imap.xxx.com:993"

	client, err := imapclient.DialTLS(imapAddr, nil)
	if err != nil {
		return err
	}
	defer client.Close()

	// 登录
	if err := client.Login("user@user.com", "xxxxxxxxx").Wait(); err != nil {
		return err
	}

	mailboxes, err := client.List("", "%", nil).Collect()
	if err != nil {
		log.Fatalf("failed to list mailboxes: %v", err)
	}
	log.Printf("Found %v mailboxes", len(mailboxes))
	for _, mbox := range mailboxes {
		log.Printf(" - %v", mbox.Mailbox)
	}

	// 使用 APPEND 命令保存邮件
	size := int64(len(emailContent))
	appendCmd := client.Append("Sent Messages", size, nil)
	if _, err := appendCmd.Write(emailContent); err != nil {
		log.Fatalf("failed to write message: %v", err)
	}
	if err := appendCmd.Close(); err != nil {
		log.Fatalf("failed to close message: %v", err)
	}
	if _, err := appendCmd.Wait(); err != nil {
		log.Fatalf("APPEND command failed: %v", err)
	}
	return err
}

到此,基本的需求已经搞定了,剩下就是根据读取的配置目录和文件,进行发送就行