docs(website): 添加简记memo产品原型和UI设计规范文档

- 新增「简记memo」一体化小程序产品原型设计文档
- 新增简记memo完整版UI视觉设计规范和界面细节
- 添加IDEA项目配置文件.gitignore
- 创建404页面HTML文件,包含响应式布局和错误提示
- 添加关于页面HTML文件,展示品牌介绍和团队信息
- 实现AES加解密工具函数,支持请求体加密
- 添加用户协议页面基础框架
This commit is contained in:
sunct
2026-07-31 14:12:32 +08:00
commit 3c3bf53ae4
115 changed files with 21304 additions and 0 deletions
+183
View File
@@ -0,0 +1,183 @@
// 邮件发送工具
package utils
import (
"crypto/tls"
"fmt"
"go.uber.org/zap"
"net/smtp"
"simple-memo/global"
"time"
)
// SendEmail 发送邮件 (阿里企业邮箱标准实现)
func SendEmail(to, subject, body string) error {
from := global.VP.GetString("email.from")
smtpHost := global.VP.GetString("email.smtp_host")
smtpPort := global.VP.GetInt("email.smtp_port")
smtpUser := global.VP.GetString("email.smtp_user")
smtpPassword := global.VP.GetString("email.smtp_password")
if smtpUser == "" || smtpPassword == "" {
return fmt.Errorf("邮件配置未完善")
}
auth := smtp.PlainAuth("", smtpUser, smtpPassword, smtpHost)
msg := []byte(fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n%s",
from, to, subject, body))
addr := fmt.Sprintf("%s:%d", smtpHost, smtpPort)
var client *smtp.Client
var err error
switch smtpPort {
case 465:
tlsConfig := &tls.Config{
ServerName: smtpHost,
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
global.Logger.Error("TLS连接失败", zap.String("host", smtpHost), zap.Int("port", smtpPort), zap.Error(err))
return fmt.Errorf("TLS连接失败: %v", err)
}
defer conn.Close()
client, err = smtp.NewClient(conn, smtpHost)
if err != nil {
global.Logger.Error("创建SMTP客户端失败", zap.Error(err))
return fmt.Errorf("创建SMTP客户端失败: %v", err)
}
defer client.Quit()
if err = client.Auth(auth); err != nil {
global.Logger.Error("SMTP认证失败", zap.Error(err))
return fmt.Errorf("SMTP认证失败: %v", err)
}
if err = client.Mail(smtpUser); err != nil {
global.Logger.Error("设置发件人失败", zap.Error(err))
return fmt.Errorf("设置发件人失败: %v", err)
}
if err = client.Rcpt(to); err != nil {
global.Logger.Error("设置收件人失败", zap.String("to", to), zap.Error(err))
return fmt.Errorf("设置收件人失败: %v", err)
}
w, err := client.Data()
if err != nil {
global.Logger.Error("获取数据写入器失败", zap.Error(err))
return fmt.Errorf("获取数据写入器失败: %v", err)
}
_, err = w.Write(msg)
if err != nil {
w.Close()
global.Logger.Error("写入邮件内容失败", zap.Error(err))
return fmt.Errorf("写入邮件内容失败: %v", err)
}
err = w.Close()
if err != nil {
global.Logger.Error("关闭写入器失败", zap.Error(err))
return fmt.Errorf("关闭写入器失败: %v", err)
}
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
return nil
case 587:
client, err = smtp.Dial(addr)
if err != nil {
global.Logger.Error("连接SMTP服务器失败", zap.String("addr", addr), zap.Error(err))
return fmt.Errorf("连接SMTP服务器失败: %v", err)
}
defer client.Close()
if err = client.StartTLS(&tls.Config{ServerName: smtpHost}); err != nil {
global.Logger.Error("启动TLS失败", zap.Error(err))
return fmt.Errorf("启动TLS失败: %v", err)
}
if err = client.Auth(auth); err != nil {
global.Logger.Error("SMTP认证失败", zap.Error(err))
return fmt.Errorf("SMTP认证失败: %v", err)
}
if err = client.Mail(smtpUser); err != nil {
global.Logger.Error("设置发件人失败", zap.Error(err))
return fmt.Errorf("设置发件人失败: %v", err)
}
if err = client.Rcpt(to); err != nil {
global.Logger.Error("设置收件人失败", zap.String("to", to), zap.Error(err))
return fmt.Errorf("设置收件人失败: %v", err)
}
w, err := client.Data()
if err != nil {
global.Logger.Error("获取数据写入器失败", zap.Error(err))
return fmt.Errorf("获取数据写入器失败: %v", err)
}
_, err = w.Write(msg)
if err != nil {
w.Close()
global.Logger.Error("写入邮件内容失败", zap.Error(err))
return fmt.Errorf("写入邮件内容失败: %v", err)
}
err = w.Close()
if err != nil {
global.Logger.Error("关闭写入器失败", zap.Error(err))
return fmt.Errorf("关闭写入器失败: %v", err)
}
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
return nil
default:
err = smtp.SendMail(addr, auth, smtpUser, []string{to}, msg)
if err != nil {
global.Logger.Error("SMTP发送失败", zap.String("addr", addr), zap.Error(err))
return fmt.Errorf("SMTP发送失败: %v", err)
}
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
return nil
}
}
// SendEmailCode 发送邮箱验证码
func SendEmailCode(to, code string) error {
subject := "简记memo - 邮箱验证"
body := fmt.Sprintf(`<!DOCTYPE html><html><head><meta charset="UTF-8"><title>邮箱验证</title></head><body style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;max-width:500px;margin:0 auto;padding:20px;"><div style="background:#ffffff;border-radius:8px;padding:24px;"><div style="font-size:18px;font-weight:600;color:#333;margin-bottom:16px;">简记memo</div><p style="color:#555;font-size:14px;line-height:1.6;margin-bottom:16px;">您好,</p><p style="color:#555;font-size:14px;line-height:1.6;margin-bottom:20px;">您正在进行邮箱验证操作,以下是您的验证码:</p><div style="background:#f5f5f5;border-radius:4px;padding:16px 24px;text-align:center;margin-bottom:20px;"><span style="font-size:28px;font-weight:600;color:#333;letter-spacing:4px;">%s</span></div><p style="color:#888;font-size:12px;line-height:1.6;margin-bottom:8px;">验证码有效期为5分钟,请在有效期内完成验证。</p><p style="color:#888;font-size:12px;line-height:1.6;">如果这不是您的操作,请忽略此邮件。</p><div style="margin-top:24px;padding-top:16px;border-top:1px solid #eee;"><p style="color:#aaa;font-size:11px;">简记memo团队</p></div></div></body></html>`, code)
// 使用重试机制发送邮件
var lastErr error
for attempt := 1; attempt <= 3; attempt++ {
err := SendEmail(to, subject, body)
if err == nil {
global.Logger.Info("邮件发送成功", zap.String("to", to), zap.String("subject", subject))
return nil
}
lastErr = err
global.Logger.Warn("邮件发送失败,准备重试",
zap.String("to", to),
zap.Int("attempt", attempt),
zap.Int("maxRetries", 3),
zap.Error(err))
// 等待一段时间后重试(指数退避)
if attempt < 3 {
time.Sleep(time.Duration(attempt) * time.Second)
}
}
return fmt.Errorf("邮件发送失败,已重试3次: %v", lastErr)
}