131 lines
3.7 KiB
Go
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)
|
|
}
|
|
|