226 lines
6.2 KiB
Go
226 lines
6.2 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"html/template"
|
|
"log"
|
|
"net"
|
|
"net/smtp"
|
|
"strings"
|
|
)
|
|
|
|
// SMTPConfig 邮件发送配置
|
|
type SMTPConfig struct {
|
|
Host string // SMTP服务器地址
|
|
Port int // SMTP端口
|
|
Username string // 发件人邮箱
|
|
Password string // SMTP授权密码
|
|
}
|
|
|
|
// 默认腾讯企业邮配置(可根据实际情况修改)
|
|
var DefaultSMTPConfig = SMTPConfig{
|
|
Host: "smtp.qcloudmail.com",
|
|
Port: 465,
|
|
Username: "1@seersun.com",
|
|
Password: "z1Tq6v45vXbYQGBrqOUE",
|
|
}
|
|
|
|
// VerificationCodeData 验证码邮件模板数据
|
|
type VerificationCodeData struct {
|
|
Code string // 验证码
|
|
ValidMin int // 有效期(分钟)
|
|
Platform string // 平台名称
|
|
Hotline string // 客服热线
|
|
}
|
|
|
|
// 验证码邮件HTML模板
|
|
const verificationCodeTemplate = `<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>验证码通知</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; font-family: "Microsoft YaHei", Arial, sans-serif; }
|
|
body { background: #f5f7fa; padding: 20px 0; }
|
|
.email-container { max-width: 550px; margin: 0 auto; background: #fff; border-radius: 12px; box-shadow: 0 3px 15px rgba(0,0,0,0.05); overflow: hidden; }
|
|
.email-header { background: linear-gradient(135deg, #667eea, #764ba2); padding: 28px; text-align: center; }
|
|
.header-icon { width: 48px; height: 48px; margin: 0 auto 12px; background: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
|
|
.icon-code { color: #667eea; font-size: 24px; font-weight: 700; }
|
|
.header-title { color: #fff; font-size: 20px; font-weight: 600; }
|
|
.email-content { padding: 30px 24px; text-align: center; }
|
|
.content-desc { font-size: 15px; color: #666; line-height: 1.8; margin-bottom: 28px; }
|
|
.code-box { display: inline-block; background: #f8f9fa; border-radius: 8px; padding: 16px 32px; margin-bottom: 28px; }
|
|
.verify-code { font-size: 28px; color: #667eea; font-weight: 700; letter-spacing: 8px; }
|
|
.warning-box { background: #fff8e1; border-radius: 8px; padding: 14px; margin: 0 20px 24px; }
|
|
.warning-text { font-size: 13px; color: #e65100; line-height: 1.6; }
|
|
.footer-note { font-size: 12px; color: #999; line-height: 1.5; }
|
|
.email-footer { background: #f8f9fa; padding: 20px; text-align: center; font-size: 11px; color: #999; margin-top: 16px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="email-container">
|
|
<!-- 邮件头部 -->
|
|
<div class="email-header">
|
|
<div class="header-icon">
|
|
<span class="icon-code">注册</span>
|
|
</div>
|
|
<div class="header-title">验证码通知</div>
|
|
</div>
|
|
<!-- 邮件内容 -->
|
|
<div class="email-content">
|
|
<p class="content-desc">尊敬的用户,您好!您正在进行身份验证操作,本次验证码如下:</p>
|
|
<!-- 验证码展示框 -->
|
|
<div class="code-box">
|
|
<span class="verify-code">{{.Code}}</span>
|
|
</div>
|
|
<!-- 提醒框 -->
|
|
<div class="warning-box">
|
|
<p class="warning-text">⚠ 验证码有效期为{{.ValidMin}}分钟,请尽快完成验证。如非本人操作,请忽略此邮件。</p>
|
|
</div>
|
|
<p class="footer-note">为保障您的账号安全,请勿将验证码泄露给他人。</p>
|
|
</div>
|
|
<!-- 邮件底部 -->
|
|
<div class="email-footer">
|
|
<p>© 2025 {{.Platform}} 版权所有 | 官网:{{.Hotline}}</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>`
|
|
|
|
// SendVerificationCode 发送验证码邮件
|
|
// to: 收件人邮箱
|
|
// code: 验证码
|
|
// 返回错误信息
|
|
func SendVerificationCode(to, code string) error {
|
|
// 模板数据
|
|
data := VerificationCodeData{
|
|
Code: code,
|
|
ValidMin: 10, // 有效期10分钟
|
|
Platform: "骄阳号", // 可替换为实际平台名称
|
|
Hotline: "seersun.com", // 可替换为实际客服热线
|
|
}
|
|
|
|
// 生成邮件内容
|
|
body, err := renderTemplate(verificationCodeTemplate, data)
|
|
if err != nil {
|
|
return fmt.Errorf("渲染模板失败: %w", err)
|
|
}
|
|
|
|
// 发送邮件
|
|
return sendMail(
|
|
DefaultSMTPConfig,
|
|
to,
|
|
"验证码通知",
|
|
body,
|
|
)
|
|
}
|
|
|
|
// renderTemplate 渲染模板
|
|
func renderTemplate(tplStr string, data interface{}) (string, error) {
|
|
tpl, err := template.New("verification").Parse(tplStr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
buf := new(strings.Builder)
|
|
if err := tpl.Execute(buf, data); err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// sendMail 发送邮件核心函数
|
|
func sendMail(config SMTPConfig, to, subject, body string) error {
|
|
// 构建邮件头部
|
|
header := make(map[string]string)
|
|
header["From"] = fmt.Sprintf("骄阳号 <%s>", config.Username)
|
|
header["To"] = to
|
|
header["Subject"] = subject
|
|
header["Content-Type"] = "text/html; charset=UTF-8"
|
|
|
|
// 拼接邮件内容
|
|
message := ""
|
|
for k, v := range header {
|
|
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
|
}
|
|
message += "\r\n" + body
|
|
|
|
// 认证信息
|
|
auth := smtp.PlainAuth(
|
|
"",
|
|
config.Username,
|
|
config.Password,
|
|
config.Host,
|
|
)
|
|
|
|
// 发送邮件
|
|
addr := fmt.Sprintf("%s:%d", config.Host, config.Port)
|
|
return SendMailWithTLS(addr, auth, config.Username, []string{to}, []byte(message))
|
|
}
|
|
|
|
// Dial 创建TLS连接的SMTP客户端
|
|
func Dial(addr string) (*smtp.Client, error) {
|
|
conn, err := tls.Dial("tcp", addr, nil)
|
|
if err != nil {
|
|
log.Println("tls.Dial Error:", err)
|
|
return nil, err
|
|
}
|
|
|
|
host, _, _ := net.SplitHostPort(addr)
|
|
return smtp.NewClient(conn, host)
|
|
}
|
|
|
|
// SendMailWithTLS 使用TLS发送邮件
|
|
func SendMailWithTLS(addr string, auth smtp.Auth, from string,
|
|
to []string, msg []byte) (err error) {
|
|
c, err := Dial(addr)
|
|
if err != nil {
|
|
log.Println("创建SMTP客户端失败:", err)
|
|
return err
|
|
}
|
|
defer c.Close()
|
|
|
|
if auth != nil {
|
|
if ok, _ := c.Extension("AUTH"); ok {
|
|
if err = c.Auth(auth); err != nil {
|
|
log.Println("认证失败:", err)
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if err = c.Mail(from); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, addr := range to {
|
|
if err = c.Rcpt(addr); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
w, err := c.Data()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = w.Write(msg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.Quit()
|
|
}
|
|
|
|
// TestSendVerificationCode 测试发送验证码邮件
|
|
func TestSendVerificationCode() error {
|
|
// 测试发送验证码为123456的邮件
|
|
return SendVerificationCode("3361562035@qq.com", "123456")
|
|
}
|