go-platform/services/login_verify_code.go
2026-04-02 00:05:03 +08:00

131 lines
3.7 KiB
Go

package services
import (
"errors"
"fmt"
"math/rand"
"strings"
"sync"
"time"
"server/models"
)
type loginCodeItem struct {
Code string
Channel string
ExpiredAt time.Time
}
var loginCodeStore sync.Map
func codeKey(account, channel string) string {
return strings.ToLower(strings.TrimSpace(account)) + "|" + strings.TrimSpace(channel)
}
func SendPlatformLoginCode(account, channel string) error {
account = strings.TrimSpace(account)
channel = strings.TrimSpace(channel)
if account == "" {
return errors.New("账号不能为空")
}
if channel != "sms" && channel != "email" {
return errors.New("仅支持短信或邮箱验证码")
}
var u models.AdminUser
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("account", account).One(&u); err != nil {
return errors.New("用户不存在")
}
if u.Status == 0 {
return errors.New("账号已禁用")
}
if channel == "sms" && (u.Phone == nil || strings.TrimSpace(*u.Phone) == "") {
return errors.New("该账号未绑定手机号")
}
if channel == "email" && (u.Email == nil || strings.TrimSpace(*u.Email) == "") {
return errors.New("该账号未绑定邮箱")
}
rand.Seed(time.Now().UnixNano())
code := fmt.Sprintf("%06d", rand.Intn(1000000))
loginCodeStore.Store(codeKey(account, channel), loginCodeItem{
Code: code,
Channel: channel,
ExpiredAt: time.Now().Add(5 * time.Minute),
})
// TODO: 接入短信/邮箱发送通道。当前阶段只做服务端验证码校验链路。
return nil
}
func VerifyPlatformLoginCode(account, channel, code string) error {
account = strings.TrimSpace(account)
channel = strings.TrimSpace(channel)
code = strings.TrimSpace(code)
if account == "" || code == "" {
return errors.New("验证码不能为空")
}
val, ok := loginCodeStore.Load(codeKey(account, channel))
if !ok {
return errors.New("验证码不存在或已失效")
}
item, ok := val.(loginCodeItem)
if !ok {
return errors.New("验证码状态异常")
}
if time.Now().After(item.ExpiredAt) {
loginCodeStore.Delete(codeKey(account, channel))
return errors.New("验证码已过期")
}
if item.Code != code {
return errors.New("验证码错误")
}
loginCodeStore.Delete(codeKey(account, channel))
return nil
}
func SendBackendLoginCode(tenantName, account, channel string) error {
tenantName = strings.TrimSpace(tenantName)
account = strings.TrimSpace(account)
channel = strings.TrimSpace(channel)
if tenantName == "" || account == "" {
return errors.New("租户名称和账号不能为空")
}
if channel != "sms" && channel != "email" {
return errors.New("仅支持短信或邮箱验证码")
}
var tenant models.Tenant
if err := models.Orm.QueryTable(new(models.Tenant)).Filter("tenant_name", tenantName).One(&tenant); err != nil {
return errors.New("租户不存在")
}
var user models.TenantUser
if err := models.Orm.QueryTable(new(models.TenantUser)).
Filter("tid", tenant.ID).
Filter("account", account).
One(&user); err != nil {
return errors.New("用户不存在")
}
if user.Status == 0 {
return errors.New("账号已禁用")
}
if channel == "sms" && (user.Phone == nil || strings.TrimSpace(*user.Phone) == "") {
return errors.New("该账号未绑定手机号")
}
if channel == "email" && (user.Email == nil || strings.TrimSpace(*user.Email) == "") {
return errors.New("该账号未绑定邮箱")
}
rand.Seed(time.Now().UnixNano())
code := fmt.Sprintf("%06d", rand.Intn(1000000))
loginCodeStore.Store(codeKey(tenantName+"#"+account, channel), loginCodeItem{
Code: code,
Channel: channel,
ExpiredAt: time.Now().Add(5 * time.Minute),
})
return nil
}
func VerifyBackendLoginCode(tenantName, account, channel, code string) error {
return VerifyPlatformLoginCode(tenantName+"#"+account, channel, code)
}