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) }