更新短信功能

This commit is contained in:
2026-04-01 15:15:21 +08:00
parent 1314e18142
commit 5ed24a8003
24 changed files with 2267 additions and 93 deletions
+50 -21
View File
@@ -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
}
+126
View File
@@ -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)
}
+77
View File
@@ -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
}