更新短信功能
This commit is contained in:
+50
-21
@@ -11,11 +11,43 @@ import (
|
||||
)
|
||||
|
||||
type PlatformLoginUser struct {
|
||||
ID uint64
|
||||
Account string
|
||||
Name string
|
||||
Rid uint64
|
||||
Avatar string
|
||||
ID uint64
|
||||
Account string
|
||||
Name string
|
||||
Rid uint64
|
||||
Avatar string
|
||||
RoleName string
|
||||
}
|
||||
|
||||
func adminRoleNameByID(roleID uint64) string {
|
||||
if roleID == 0 {
|
||||
return ""
|
||||
}
|
||||
var role models.AdminRole
|
||||
err := models.Orm.QueryTable(new(models.AdminRole)).Filter("id", roleID).One(&role)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return role.Name
|
||||
}
|
||||
|
||||
func toPlatformLoginUser(user *models.AdminUser) *PlatformLoginUser {
|
||||
name := ""
|
||||
if user.Name != nil {
|
||||
name = *user.Name
|
||||
}
|
||||
avatar := ""
|
||||
if user.Avatar != nil {
|
||||
avatar = *user.Avatar
|
||||
}
|
||||
return &PlatformLoginUser{
|
||||
ID: user.ID,
|
||||
Account: user.Account,
|
||||
Name: name,
|
||||
Rid: user.RoleID,
|
||||
Avatar: avatar,
|
||||
RoleName: adminRoleNameByID(user.RoleID),
|
||||
}
|
||||
}
|
||||
|
||||
func md5Hex(s string) string {
|
||||
@@ -23,7 +55,7 @@ func md5Hex(s string) string {
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// PlatformLogin 平台登录业务(仅允许平台用户 yz_admin_user 登录)
|
||||
// PlatformLogin 平台登录业务(仅允许平台用户 yz_system_admin_user 登录)
|
||||
func PlatformLogin(account, password string) (string, *PlatformLoginUser, error) {
|
||||
account = strings.TrimSpace(account)
|
||||
password = strings.TrimSpace(password)
|
||||
@@ -52,21 +84,18 @@ func PlatformLogin(account, password string) (string, *PlatformLoginUser, error)
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
name := ""
|
||||
if user.Name != nil {
|
||||
name = *user.Name
|
||||
}
|
||||
avatar := ""
|
||||
if user.Avatar != nil {
|
||||
avatar = *user.Avatar
|
||||
}
|
||||
loginUser := &PlatformLoginUser{
|
||||
ID: user.ID,
|
||||
Account: user.Account,
|
||||
Name: name,
|
||||
Rid: user.RoleID,
|
||||
Avatar: avatar,
|
||||
}
|
||||
loginUser := toPlatformLoginUser(&user)
|
||||
return token, loginUser, nil
|
||||
}
|
||||
|
||||
// PlatformGetCurrentUser 根据平台管理员用户 ID 返回登录用户信息(含角色名称)
|
||||
func PlatformGetCurrentUser(uid uint64) (*PlatformLoginUser, error) {
|
||||
u, err := models.GetAdminUserByID(uid)
|
||||
if err != nil {
|
||||
return nil, errors.New("用户不存在")
|
||||
}
|
||||
if u.Status == 0 {
|
||||
return nil, errors.New("账号已禁用")
|
||||
}
|
||||
return toPlatformLoginUser(u), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SMTPConfig 发送邮件所需参数(与 yz_system_email 字段对应)
|
||||
type SMTPConfig struct {
|
||||
FromAddress string
|
||||
FromName string
|
||||
Host string
|
||||
Port uint
|
||||
Password string
|
||||
Encryption string // ssl / tls / none
|
||||
Timeout uint // 秒
|
||||
}
|
||||
|
||||
// SendTestEmailSMTP 发送一封简单测试邮件(纯文本 UTF-8)
|
||||
func SendTestEmailSMTP(cfg SMTPConfig, to string) error {
|
||||
to = strings.TrimSpace(to)
|
||||
if to == "" {
|
||||
return fmt.Errorf("收件人不能为空")
|
||||
}
|
||||
if cfg.Host == "" || cfg.FromAddress == "" {
|
||||
return fmt.Errorf("SMTP 主机或发件人不能为空")
|
||||
}
|
||||
if cfg.Port == 0 {
|
||||
cfg.Port = 465
|
||||
}
|
||||
timeout := cfg.Timeout
|
||||
if timeout == 0 {
|
||||
timeout = 30
|
||||
}
|
||||
d := net.Dialer{Timeout: time.Duration(timeout) * time.Second}
|
||||
addr := net.JoinHostPort(cfg.Host, strconv.FormatUint(uint64(cfg.Port), 10))
|
||||
enc := strings.ToLower(strings.TrimSpace(cfg.Encryption))
|
||||
if enc == "" {
|
||||
enc = "ssl"
|
||||
}
|
||||
|
||||
var client *smtp.Client
|
||||
var err error
|
||||
|
||||
switch enc {
|
||||
case "ssl":
|
||||
conn, derr := tls.DialWithDialer(&d, "tcp", addr, &tls.Config{ServerName: cfg.Host, MinVersion: tls.VersionTLS12})
|
||||
if derr != nil {
|
||||
return fmt.Errorf("连接 SMTP 失败: %w", derr)
|
||||
}
|
||||
defer conn.Close()
|
||||
client, err = smtp.NewClient(conn, cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SMTP 握手失败: %w", err)
|
||||
}
|
||||
case "tls":
|
||||
conn, derr := d.Dial("tcp", addr)
|
||||
if derr != nil {
|
||||
return fmt.Errorf("连接 SMTP 失败: %w", derr)
|
||||
}
|
||||
defer conn.Close()
|
||||
client, err = smtp.NewClient(conn, cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SMTP 握手失败: %w", err)
|
||||
}
|
||||
if ok, _ := client.Extension("STARTTLS"); ok {
|
||||
if err = client.StartTLS(&tls.Config{ServerName: cfg.Host, MinVersion: tls.VersionTLS12}); err != nil {
|
||||
_ = client.Close()
|
||||
return fmt.Errorf("STARTTLS 失败: %w", err)
|
||||
}
|
||||
}
|
||||
case "none":
|
||||
conn, derr := d.Dial("tcp", addr)
|
||||
if derr != nil {
|
||||
return fmt.Errorf("连接 SMTP 失败: %w", derr)
|
||||
}
|
||||
defer conn.Close()
|
||||
client, err = smtp.NewClient(conn, cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SMTP 握手失败: %w", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("不支持的加密方式: %s", cfg.Encryption)
|
||||
}
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
auth := smtp.PlainAuth("", cfg.FromAddress, cfg.Password, cfg.Host)
|
||||
if err = client.Auth(auth); err != nil {
|
||||
return fmt.Errorf("SMTP 认证失败: %w", err)
|
||||
}
|
||||
if err = client.Mail(cfg.FromAddress); err != nil {
|
||||
return fmt.Errorf("MAIL FROM 失败: %w", err)
|
||||
}
|
||||
if err = client.Rcpt(to); err != nil {
|
||||
return fmt.Errorf("RCPT TO 失败: %w", err)
|
||||
}
|
||||
wc, err := client.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("DATA 失败: %w", err)
|
||||
}
|
||||
fromName := strings.TrimSpace(cfg.FromName)
|
||||
subject := "平台邮箱测试"
|
||||
body := "这是一封来自管理后台「邮箱管理」的测试邮件。\r\nThis is a test email from the platform email settings.\r\n"
|
||||
headers := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: 8bit\r\n\r\n",
|
||||
formatFromHeader(fromName, cfg.FromAddress), to, subject)
|
||||
if _, err = wc.Write([]byte(headers + body)); err != nil {
|
||||
return fmt.Errorf("写入邮件内容失败: %w", err)
|
||||
}
|
||||
if err = wc.Close(); err != nil {
|
||||
return fmt.Errorf("结束 DATA 失败: %w", err)
|
||||
}
|
||||
return client.Quit()
|
||||
}
|
||||
|
||||
func formatFromHeader(name, addr string) string {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return addr
|
||||
}
|
||||
return fmt.Sprintf("%s <%s>", name, addr)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"server/models"
|
||||
)
|
||||
|
||||
// ListSystemEmails 返回全部邮箱配置(按 id 升序,通常仅一条)
|
||||
func ListSystemEmails() ([]models.SystemEmail, error) {
|
||||
var rows []models.SystemEmail
|
||||
_, err := models.Orm.QueryTable(new(models.SystemEmail)).OrderBy("id").All(&rows)
|
||||
return rows, err
|
||||
}
|
||||
|
||||
// UpsertFirstSystemEmail 若已有记录则更新第一条,否则插入
|
||||
func UpsertFirstSystemEmail(fromAddress string, fromName *string, host string, port uint, password string, encryption string, timeout uint, status int8, remark *string) error {
|
||||
if encryption == "" {
|
||||
encryption = "ssl"
|
||||
}
|
||||
if port == 0 {
|
||||
port = 465
|
||||
}
|
||||
if timeout == 0 {
|
||||
timeout = 30
|
||||
}
|
||||
if status == 0 {
|
||||
status = 1
|
||||
}
|
||||
fromAddress = strings.TrimSpace(fromAddress)
|
||||
host = strings.TrimSpace(host)
|
||||
|
||||
cnt, err := models.Orm.QueryTable(new(models.SystemEmail)).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cnt == 0 {
|
||||
if strings.TrimSpace(password) == "" {
|
||||
return fmt.Errorf("首次保存必须填写授权码/密码")
|
||||
}
|
||||
row := &models.SystemEmail{
|
||||
FromAddress: fromAddress,
|
||||
FromName: fromName,
|
||||
Host: host,
|
||||
Port: port,
|
||||
Password: strings.TrimSpace(password),
|
||||
Encryption: encryption,
|
||||
Timeout: timeout,
|
||||
Status: status,
|
||||
Remark: remark,
|
||||
}
|
||||
_, err = models.Orm.Insert(row)
|
||||
return err
|
||||
}
|
||||
|
||||
var first models.SystemEmail
|
||||
if err := models.Orm.QueryTable(new(models.SystemEmail)).OrderBy("id").Limit(1).One(&first); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
up := map[string]interface{}{
|
||||
"from_address": fromAddress,
|
||||
"from_name": fromName,
|
||||
"host": host,
|
||||
"port": port,
|
||||
"encryption": encryption,
|
||||
"timeout": timeout,
|
||||
"status": status,
|
||||
"remark": remark,
|
||||
}
|
||||
if strings.TrimSpace(password) != "" {
|
||||
up["password"] = strings.TrimSpace(password)
|
||||
}
|
||||
_, err = models.Orm.QueryTable(new(models.SystemEmail)).Filter("id", first.ID).Update(up)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user