✨ smtp服务
This commit is contained in:
@@ -27,3 +27,23 @@ type ConfigPublicGetReq struct {
|
||||
g.Meta `path:"/config/public/{key}" method:"get" tags:"Config" summary:"获取公开配置(无需认证)" auth:"false"`
|
||||
Key string `json:"key" v:"required#配置键不能为空"`
|
||||
}
|
||||
|
||||
// SMTP 配置
|
||||
type SmtpConfigGetReq struct {
|
||||
g.Meta `path:"/config/smtp" method:"get" tags:"Config" summary:"获取SMTP配置" permission:"sys:config:edit"`
|
||||
}
|
||||
|
||||
type SmtpConfigUpdateReq struct {
|
||||
g.Meta `path:"/config/smtp" method:"put" tags:"Config" summary:"更新SMTP配置" permission:"sys:config:edit"`
|
||||
SmtpHost string `json:"smtpHost" v:"required#SMTP服务器地址不能为空"`
|
||||
SmtpPort int `json:"smtpPort" v:"required#SMTP端口不能为空"`
|
||||
SmtpUsername string `json:"smtpUsername" v:"required#SMTP用户名不能为空"`
|
||||
SmtpPassword string `json:"smtpPassword" v:"required#SMTP密码不能为空"`
|
||||
FromEmail string `json:"fromEmail" v:"required#发件人邮箱不能为空"`
|
||||
FromName string `json:"fromName" v:"required#发件人名称不能为空"`
|
||||
}
|
||||
|
||||
type SmtpTestReq struct {
|
||||
g.Meta `path:"/config/smtp/test" method:"post" tags:"Config" summary:"发送测试邮件" permission:"sys:config:edit"`
|
||||
ToEmail string `json:"toEmail" v:"required|email#收件人邮箱不能为空|请输入有效邮箱"`
|
||||
}
|
||||
|
||||
@@ -46,3 +46,21 @@ func (c ConfigCtrlV1) UpdatePriceConfig(ctx context.Context, req *v1.PriceConfig
|
||||
func NewConfigCtrlV1() IConfigCtrlV1 {
|
||||
return &ConfigCtrlV1{}
|
||||
}
|
||||
|
||||
// GetSmtpConfig 获取SMTP配置
|
||||
func (c ConfigCtrlV1) GetSmtpConfig(ctx context.Context, req *v1.SmtpConfigGetReq) (res *utils.Response[any], err error) {
|
||||
defer utils.Pass(&err)
|
||||
return service.Global.Config.GetSmtpConfig(ctx, req), nil
|
||||
}
|
||||
|
||||
// UpdateSmtpConfig 更新SMTP配置
|
||||
func (c ConfigCtrlV1) UpdateSmtpConfig(ctx context.Context, req *v1.SmtpConfigUpdateReq) (res *utils.Response[any], err error) {
|
||||
defer utils.Pass(&err)
|
||||
return service.Global.Config.UpdateSmtpConfig(ctx, req), nil
|
||||
}
|
||||
|
||||
// TestSmtp 发送测试邮件
|
||||
func (c ConfigCtrlV1) TestSmtp(ctx context.Context, req *v1.SmtpTestReq) (res *utils.Response[any], err error) {
|
||||
defer utils.Pass(&err)
|
||||
return service.Global.Config.TestSmtp(ctx, req), nil
|
||||
}
|
||||
|
||||
@@ -14,4 +14,7 @@ type IConfigCtrlV1 interface {
|
||||
Update(ctx context.Context, req *v1.ConfigUpdateReq) (*utils.Response[any], error)
|
||||
GetPriceConfig(ctx context.Context, req *v1.PriceConfigGetReq) (*utils.Response[*v1.PriceConfigResp], error)
|
||||
UpdatePriceConfig(ctx context.Context, req *v1.PriceConfigUpdateReq) (*utils.Response[any], error)
|
||||
GetSmtpConfig(ctx context.Context, req *v1.SmtpConfigGetReq) (*utils.Response[any], error)
|
||||
UpdateSmtpConfig(ctx context.Context, req *v1.SmtpConfigUpdateReq) (*utils.Response[any], error)
|
||||
TestSmtp(ctx context.Context, req *v1.SmtpTestReq) (*utils.Response[any], error)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,18 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
v1 "likedream-goframe/internal/api/v1"
|
||||
cst2 "likedream-goframe/internal/common/cst"
|
||||
"likedream-goframe/internal/common/db"
|
||||
errors2 "likedream-goframe/internal/common/errors"
|
||||
"likedream-goframe/internal/ent"
|
||||
"likedream-goframe/internal/ent/sysconfig"
|
||||
"likedream-goframe/internal/service/email"
|
||||
"likedream-goframe/internal/utils"
|
||||
"time"
|
||||
|
||||
mail "github.com/wneessen/go-mail"
|
||||
)
|
||||
|
||||
type ConfigServiceV1 struct {
|
||||
@@ -189,3 +195,77 @@ func (s *ConfigServiceV1) upsertConfig(ctx context.Context, key, value, descript
|
||||
SetValue(value).
|
||||
ExecX(ctx)
|
||||
}
|
||||
|
||||
// GetSmtpConfig 获取SMTP配置
|
||||
func (s *ConfigServiceV1) GetSmtpConfig(ctx context.Context, req *v1.SmtpConfigGetReq) *utils.Response[any] {
|
||||
keys := []string{
|
||||
cst2.ConfigEmailSMTPHost,
|
||||
cst2.ConfigEmailSMTPPort,
|
||||
cst2.ConfigEmailSMTPUsername,
|
||||
cst2.ConfigEmailSMTPPassword,
|
||||
cst2.ConfigEmailFromEmail,
|
||||
cst2.ConfigEmailFromName,
|
||||
}
|
||||
result := make(map[string]string)
|
||||
for _, key := range keys {
|
||||
config, err := db.Global.SysConfig.Query().Where(sysconfig.KeyEQ(key)).Only(ctx)
|
||||
if err == nil {
|
||||
result[key] = config.Value
|
||||
}
|
||||
}
|
||||
return utils.SuccessWithData[any](map[string]string{
|
||||
"smtpHost": result[cst2.ConfigEmailSMTPHost],
|
||||
"smtpPort": result[cst2.ConfigEmailSMTPPort],
|
||||
"smtpUsername": result[cst2.ConfigEmailSMTPUsername],
|
||||
"smtpPassword": result[cst2.ConfigEmailSMTPPassword],
|
||||
"fromEmail": result[cst2.ConfigEmailFromEmail],
|
||||
"fromName": result[cst2.ConfigEmailFromName],
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateSmtpConfig 更新SMTP配置
|
||||
func (s *ConfigServiceV1) UpdateSmtpConfig(ctx context.Context, req *v1.SmtpConfigUpdateReq) *utils.Response[any] {
|
||||
configs := map[string]string{
|
||||
cst2.ConfigEmailSMTPHost: req.SmtpHost,
|
||||
cst2.ConfigEmailSMTPPort: fmt.Sprintf("%d", req.SmtpPort),
|
||||
cst2.ConfigEmailSMTPUsername: req.SmtpUsername,
|
||||
cst2.ConfigEmailSMTPPassword: req.SmtpPassword,
|
||||
cst2.ConfigEmailFromEmail: req.FromEmail,
|
||||
cst2.ConfigEmailFromName: req.FromName,
|
||||
}
|
||||
for key, value := range configs {
|
||||
exists, _ := db.Global.SysConfig.Query().Where(sysconfig.KeyEQ(key)).Exist(ctx)
|
||||
if exists {
|
||||
db.Global.SysConfig.Update().Where(sysconfig.KeyEQ(key)).SetValue(value).ExecX(ctx)
|
||||
} else {
|
||||
db.Global.SysConfig.Create().SetKey(key).SetValue(value).SetDescription("SMTP邮件配置").ExecX(ctx)
|
||||
}
|
||||
}
|
||||
return utils.SuccessWithMsg("SMTP配置保存成功")
|
||||
}
|
||||
|
||||
// TestSmtp 发送测试邮件
|
||||
func (s *ConfigServiceV1) TestSmtp(ctx context.Context, req *v1.SmtpTestReq) *utils.Response[any] {
|
||||
emailService := email.NewV1()
|
||||
// 直接同步发送测试邮件,不走队列
|
||||
msg := mail.NewMsg()
|
||||
e := msg.FromFormat(emailService.GetConfig().FromName, emailService.GetConfig().FromEmail)
|
||||
utils.OkOrPanic(e)
|
||||
e = msg.AddTo(req.ToEmail)
|
||||
utils.OkOrPanic(e)
|
||||
msg.Subject("若梦系统 - SMTP测试邮件")
|
||||
msg.SetBodyString(mail.TypeTextHTML, "<h2>SMTP配置测试成功</h2><p>如果您收到此邮件,说明SMTP配置正确。</p>")
|
||||
|
||||
cfg := emailService.GetConfig()
|
||||
client, e := mail.NewClient(cfg.SMTPHost, mail.WithPort(cfg.SMTPPort),
|
||||
mail.WithSMTPAuth(mail.SMTPAuthLogin),
|
||||
mail.WithTLSPortPolicy(mail.TLSMandatory),
|
||||
mail.WithTimeout(30*time.Second),
|
||||
mail.WithUsername(cfg.SMTPUsername), mail.WithPassword(cfg.SMTPPassword))
|
||||
utils.OkOrPanic(e)
|
||||
defer client.Close()
|
||||
|
||||
e = client.DialAndSendWithContext(ctx, msg)
|
||||
utils.OkOrPanic(e)
|
||||
return utils.SuccessWithMsg("测试邮件发送成功")
|
||||
}
|
||||
|
||||
@@ -7,14 +7,17 @@ import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"html/template"
|
||||
db "likedream-goframe/internal/common/db"
|
||||
"strconv"
|
||||
|
||||
cst2 "likedream-goframe/internal/common/cst"
|
||||
"likedream-goframe/internal/ent/sysconfig"
|
||||
"likedream-goframe/internal/framework/logger"
|
||||
email2 "likedream-goframe/internal/framework/model/email"
|
||||
"likedream-goframe/internal/framework/queue"
|
||||
"likedream-goframe/internal/utils"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/samber/oops"
|
||||
@@ -25,7 +28,6 @@ import (
|
||||
var templateFiles embed.FS
|
||||
|
||||
type EmailServiceV1 struct {
|
||||
config EmailConfig
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
@@ -38,20 +40,32 @@ type EmailConfig struct {
|
||||
}
|
||||
|
||||
func NewV1() *EmailServiceV1 {
|
||||
ctx := gctx.New()
|
||||
cfg := g.Cfg()
|
||||
|
||||
config := EmailConfig{
|
||||
SMTPHost: cfg.MustGetWithEnv(ctx, cst2.ConfigEmailSMTPHost).String(),
|
||||
SMTPPort: cfg.MustGetWithEnv(ctx, cst2.ConfigEmailSMTPPort).Int(),
|
||||
SMTPUsername: cfg.MustGetWithEnv(ctx, cst2.ConfigEmailSMTPUsername).String(),
|
||||
SMTPPassword: cfg.MustGetWithEnv(ctx, cst2.ConfigEmailSMTPPassword).String(),
|
||||
FromEmail: cfg.MustGetWithEnv(ctx, cst2.ConfigEmailFromEmail).String(),
|
||||
FromName: cfg.MustGetWithEnv(ctx, cst2.ConfigEmailFromName).String(),
|
||||
return &EmailServiceV1{}
|
||||
}
|
||||
|
||||
return &EmailServiceV1{
|
||||
config: config,
|
||||
// GetConfig 从数据库读取当前邮件配置
|
||||
func (s *EmailServiceV1) GetConfig() EmailConfig {
|
||||
ctx := gctx.New()
|
||||
|
||||
getVal := func(key string) string {
|
||||
config, err := db.Global.SysConfig.Query().
|
||||
Where(sysconfig.KeyEQ(key)).
|
||||
Only(ctx)
|
||||
if err == nil {
|
||||
return config.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
port, _ := strconv.Atoi(getVal(cst2.ConfigEmailSMTPPort))
|
||||
|
||||
return EmailConfig{
|
||||
SMTPHost: getVal(cst2.ConfigEmailSMTPHost),
|
||||
SMTPPort: port,
|
||||
SMTPUsername: getVal(cst2.ConfigEmailSMTPUsername),
|
||||
SMTPPassword: getVal(cst2.ConfigEmailSMTPPassword),
|
||||
FromEmail: getVal(cst2.ConfigEmailFromEmail),
|
||||
FromName: getVal(cst2.ConfigEmailFromName),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +89,8 @@ func brandCopyright() string {
|
||||
|
||||
// SendSignupEmail 发送注册成功邮件
|
||||
func (s *EmailServiceV1) SendSignupEmail(ctx context.Context, toEmail, username string, token string) *utils.Response[any] {
|
||||
if s.config.SMTPUsername == "" || s.config.SMTPPassword == "" {
|
||||
cfg := s.GetConfig()
|
||||
if cfg.SMTPUsername == "" || cfg.SMTPPassword == "" {
|
||||
logger.Logger.Error("邮件服务未配置")
|
||||
return utils.FailBusiness("邮件服务未配置")
|
||||
}
|
||||
@@ -99,7 +114,8 @@ func (s *EmailServiceV1) SendSignupEmail(ctx context.Context, toEmail, username
|
||||
|
||||
// SendTaskNotificationEmail 发送任务状态通知邮件
|
||||
func (s *EmailServiceV1) SendTaskNotificationEmail(ctx context.Context, toEmail, taskName string, status email2.TaskStatus, details map[string]string) *utils.Response[any] {
|
||||
if s.config.SMTPUsername == "" || s.config.SMTPPassword == "" {
|
||||
cfg := s.GetConfig()
|
||||
if cfg.SMTPUsername == "" || cfg.SMTPPassword == "" {
|
||||
return utils.FailBusiness("邮件服务未配置")
|
||||
}
|
||||
|
||||
@@ -119,7 +135,8 @@ func (s *EmailServiceV1) SendTaskNotificationEmail(ctx context.Context, toEmail,
|
||||
|
||||
// SendResetPasswordEmail 发送重置密码邮件
|
||||
func (s *EmailServiceV1) SendResetPasswordEmail(ctx context.Context, toEmail, username string, token string) *utils.Response[any] {
|
||||
if s.config.SMTPUsername == "" || s.config.SMTPPassword == "" {
|
||||
cfg := s.GetConfig()
|
||||
if cfg.SMTPUsername == "" || cfg.SMTPPassword == "" {
|
||||
logger.Logger.Error("邮件服务未配置")
|
||||
return utils.FailBusiness("邮件服务未配置")
|
||||
}
|
||||
@@ -143,7 +160,8 @@ func (s *EmailServiceV1) SendResetPasswordEmail(ctx context.Context, toEmail, us
|
||||
|
||||
// SendBalanceMigrateEmail 发送积分转移邮件
|
||||
func (s *EmailServiceV1) SendBalanceMigrateEmail(ctx context.Context, toEmail, username string, balance int, token string) *utils.Response[any] {
|
||||
if s.config.SMTPUsername == "" || s.config.SMTPPassword == "" {
|
||||
cfg := s.GetConfig()
|
||||
if cfg.SMTPUsername == "" || cfg.SMTPPassword == "" {
|
||||
logger.Logger.Error("邮件服务未配置")
|
||||
return utils.FailBusiness("邮件服务未配置")
|
||||
}
|
||||
@@ -167,18 +185,19 @@ func (s *EmailServiceV1) SendBalanceMigrateEmail(ctx context.Context, toEmail, u
|
||||
|
||||
// sendEmail 发送邮件的底层方法
|
||||
func (s *EmailServiceV1) sendEmail(ctx context.Context, to, subject, body string) error {
|
||||
cfg := s.GetConfig()
|
||||
payload := bytes.NewBuffer(nil)
|
||||
|
||||
e := gob.NewEncoder(payload).Encode(&email2.SendPayload{
|
||||
To: to,
|
||||
Subject: subject,
|
||||
Body: body,
|
||||
FromName: s.config.FromName,
|
||||
FromEmail: s.config.FromEmail,
|
||||
SMTPHost: s.config.SMTPHost,
|
||||
SMTPPort: s.config.SMTPPort,
|
||||
SMTPUsername: s.config.SMTPUsername,
|
||||
SMTPPassword: s.config.SMTPPassword,
|
||||
FromName: cfg.FromName,
|
||||
FromEmail: cfg.FromEmail,
|
||||
SMTPHost: cfg.SMTPHost,
|
||||
SMTPPort: cfg.SMTPPort,
|
||||
SMTPUsername: cfg.SMTPUsername,
|
||||
SMTPPassword: cfg.SMTPPassword,
|
||||
})
|
||||
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user