mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-03 23:20:26 +08:00
实现用户系统手机号码绑定和登录(商业版)
This commit is contained in:
@@ -428,6 +428,20 @@ func (this *UserDAO) CheckUserEmailPassword(tx *dbs.Tx, verifiedEmail string, en
|
||||
FindInt64Col(0)
|
||||
}
|
||||
|
||||
// CheckUserMobilePassword 检查邮箱+密码
|
||||
func (this *UserDAO) CheckUserMobilePassword(tx *dbs.Tx, verifiedEmail string, encryptedPassword string) (int64, error) {
|
||||
if len(verifiedEmail) == 0 || len(encryptedPassword) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return this.Query(tx).
|
||||
Attr("verifiedMobile", verifiedEmail).
|
||||
Attr("password", encryptedPassword).
|
||||
Attr("state", UserStateEnabled).
|
||||
Attr("isOn", true).
|
||||
ResultPk().
|
||||
FindInt64Col(0)
|
||||
}
|
||||
|
||||
// FindUserClusterId 查找用户所在集群
|
||||
func (this *UserDAO) FindUserClusterId(tx *dbs.Tx, userId int64) (int64, error) {
|
||||
return this.Query(tx).
|
||||
@@ -663,7 +677,7 @@ func (this *UserDAO) RenewUserServersState(tx *dbs.Tx, userId int64) (bool, erro
|
||||
// FindUserIdWithVerifiedEmail 使用验证后Email查找用户ID
|
||||
func (this *UserDAO) FindUserIdWithVerifiedEmail(tx *dbs.Tx, verifiedEmail string) (int64, error) {
|
||||
if len(verifiedEmail) == 0 {
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
return this.Query(tx).
|
||||
ResultPk().
|
||||
@@ -672,6 +686,18 @@ func (this *UserDAO) FindUserIdWithVerifiedEmail(tx *dbs.Tx, verifiedEmail strin
|
||||
FindInt64Col(0)
|
||||
}
|
||||
|
||||
// FindUserIdWithVerifiedMobile 使用验证后手机号码查找用户ID
|
||||
func (this *UserDAO) FindUserIdWithVerifiedMobile(tx *dbs.Tx, verifiedMobile string) (int64, error) {
|
||||
if len(verifiedMobile) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return this.Query(tx).
|
||||
ResultPk().
|
||||
State(UserStateEnabled).
|
||||
Attr("verifiedMobile", verifiedMobile).
|
||||
FindInt64Col(0)
|
||||
}
|
||||
|
||||
// UpdateUserVerifiedEmail 修改已激活邮箱
|
||||
func (this *UserDAO) UpdateUserVerifiedEmail(tx *dbs.Tx, userId int64, verifiedEmail string) error {
|
||||
if userId <= 0 {
|
||||
@@ -684,6 +710,18 @@ func (this *UserDAO) UpdateUserVerifiedEmail(tx *dbs.Tx, userId int64, verifiedE
|
||||
UpdateQuickly()
|
||||
}
|
||||
|
||||
// UpdateUserVerifiedMobile 修改已激活手机号码
|
||||
func (this *UserDAO) UpdateUserVerifiedMobile(tx *dbs.Tx, userId int64, verifiedMobile string) error {
|
||||
if userId <= 0 {
|
||||
return nil
|
||||
}
|
||||
return this.Query(tx).
|
||||
Pk(userId).
|
||||
Set("verifiedMobile", verifiedMobile).
|
||||
Set("mobileIsVerified", true).
|
||||
UpdateQuickly()
|
||||
}
|
||||
|
||||
// FindUserBandwidthAlgoForView 获取用户浏览用的带宽算法
|
||||
func (this *UserDAO) FindUserBandwidthAlgoForView(tx *dbs.Tx, userId int64, uiConfig *systemconfigs.UserUIConfig) (bandwidthAlgo string, err error) {
|
||||
bandwidthAlgo, err = this.Query(tx).
|
||||
|
||||
28
internal/db/models/user_mobile_verification_dao.go
Normal file
28
internal/db/models/user_mobile_verification_dao.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/dbs"
|
||||
)
|
||||
|
||||
type UserMobileVerificationDAO dbs.DAO
|
||||
|
||||
func NewUserMobileVerificationDAO() *UserMobileVerificationDAO {
|
||||
return dbs.NewDAO(&UserMobileVerificationDAO{
|
||||
DAOObject: dbs.DAOObject{
|
||||
DB: Tea.Env,
|
||||
Table: "edgeUserMobileVerifications",
|
||||
Model: new(UserMobileVerification),
|
||||
PkName: "id",
|
||||
},
|
||||
}).(*UserMobileVerificationDAO)
|
||||
}
|
||||
|
||||
var SharedUserMobileVerificationDAO *UserMobileVerificationDAO
|
||||
|
||||
func init() {
|
||||
dbs.OnReady(func() {
|
||||
SharedUserMobileVerificationDAO = NewUserMobileVerificationDAO()
|
||||
})
|
||||
}
|
||||
6
internal/db/models/user_mobile_verification_dao_test.go
Normal file
6
internal/db/models/user_mobile_verification_dao_test.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
)
|
||||
41
internal/db/models/user_mobile_verification_model.go
Normal file
41
internal/db/models/user_mobile_verification_model.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package models
|
||||
|
||||
import "github.com/iwind/TeaGo/dbs"
|
||||
|
||||
const (
|
||||
UserMobileVerificationField_Id dbs.FieldName = "id" // ID
|
||||
UserMobileVerificationField_Mobile dbs.FieldName = "mobile" // 手机号码
|
||||
UserMobileVerificationField_UserId dbs.FieldName = "userId" // 用户ID
|
||||
UserMobileVerificationField_Code dbs.FieldName = "code" // 激活码
|
||||
UserMobileVerificationField_CreatedAt dbs.FieldName = "createdAt" // 创建时间
|
||||
UserMobileVerificationField_IsSent dbs.FieldName = "isSent" // 是否已发送
|
||||
UserMobileVerificationField_IsVerified dbs.FieldName = "isVerified" // 是否已激活
|
||||
UserMobileVerificationField_Day dbs.FieldName = "day" // YYYYMMDD
|
||||
)
|
||||
|
||||
// UserMobileVerification 邮箱激活邮件队列
|
||||
type UserMobileVerification struct {
|
||||
Id uint64 `field:"id"` // ID
|
||||
Mobile string `field:"mobile"` // 手机号码
|
||||
UserId uint64 `field:"userId"` // 用户ID
|
||||
Code string `field:"code"` // 激活码
|
||||
CreatedAt uint64 `field:"createdAt"` // 创建时间
|
||||
IsSent bool `field:"isSent"` // 是否已发送
|
||||
IsVerified bool `field:"isVerified"` // 是否已激活
|
||||
Day string `field:"day"` // YYYYMMDD
|
||||
}
|
||||
|
||||
type UserMobileVerificationOperator struct {
|
||||
Id any // ID
|
||||
Mobile any // 手机号码
|
||||
UserId any // 用户ID
|
||||
Code any // 激活码
|
||||
CreatedAt any // 创建时间
|
||||
IsSent any // 是否已发送
|
||||
IsVerified any // 是否已激活
|
||||
Day any // YYYYMMDD
|
||||
}
|
||||
|
||||
func NewUserMobileVerificationOperator() *UserMobileVerificationOperator {
|
||||
return &UserMobileVerificationOperator{}
|
||||
}
|
||||
1
internal/db/models/user_mobile_verification_model_ext.go
Normal file
1
internal/db/models/user_mobile_verification_model_ext.go
Normal file
@@ -0,0 +1 @@
|
||||
package models
|
||||
@@ -10,6 +10,7 @@ const (
|
||||
UserField_Fullname dbs.FieldName = "fullname" // 真实姓名
|
||||
UserField_Mobile dbs.FieldName = "mobile" // 手机号
|
||||
UserField_VerifiedMobile dbs.FieldName = "verifiedMobile" // 已验证手机号
|
||||
UserField_MobileIsVerified dbs.FieldName = "mobileIsVerified" // 手机号是否已验证
|
||||
UserField_Tel dbs.FieldName = "tel" // 联系电话
|
||||
UserField_Remark dbs.FieldName = "remark" // 备注
|
||||
UserField_Email dbs.FieldName = "email" // 邮箱地址
|
||||
@@ -47,6 +48,7 @@ type User struct {
|
||||
Fullname string `field:"fullname"` // 真实姓名
|
||||
Mobile string `field:"mobile"` // 手机号
|
||||
VerifiedMobile string `field:"verifiedMobile"` // 已验证手机号
|
||||
MobileIsVerified uint8 `field:"mobileIsVerified"` // 手机号是否已验证
|
||||
Tel string `field:"tel"` // 联系电话
|
||||
Remark string `field:"remark"` // 备注
|
||||
Email string `field:"email"` // 邮箱地址
|
||||
@@ -83,6 +85,7 @@ type UserOperator struct {
|
||||
Fullname any // 真实姓名
|
||||
Mobile any // 手机号
|
||||
VerifiedMobile any // 已验证手机号
|
||||
MobileIsVerified any // 手机号是否已验证
|
||||
Tel any // 联系电话
|
||||
Remark any // 备注
|
||||
Email any // 邮箱地址
|
||||
|
||||
@@ -241,28 +241,31 @@ func (this *UserService) FindEnabledUser(ctx context.Context, req *pb.FindEnable
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.FindEnabledUserResponse{User: &pb.User{
|
||||
Id: int64(user.Id),
|
||||
Username: user.Username,
|
||||
Fullname: user.Fullname,
|
||||
Mobile: user.Mobile,
|
||||
Tel: user.Tel,
|
||||
Email: user.Email,
|
||||
VerifiedEmail: user.VerifiedEmail,
|
||||
Remark: user.Remark,
|
||||
IsOn: user.IsOn,
|
||||
CreatedAt: int64(user.CreatedAt),
|
||||
RegisteredIP: user.RegisteredIP,
|
||||
IsVerified: user.IsVerified,
|
||||
IsRejected: user.IsRejected,
|
||||
RejectReason: user.RejectReason,
|
||||
NodeCluster: pbCluster,
|
||||
IsIndividualIdentified: isIndividualIdentified,
|
||||
IsEnterpriseIdentified: isEnterpriseIdentified,
|
||||
BandwidthAlgo: user.BandwidthAlgo,
|
||||
OtpLogin: pbOtpAuth,
|
||||
Lang: user.Lang,
|
||||
}}, nil
|
||||
return &pb.FindEnabledUserResponse{
|
||||
User: &pb.User{
|
||||
Id: int64(user.Id),
|
||||
Username: user.Username,
|
||||
Fullname: user.Fullname,
|
||||
Mobile: user.Mobile,
|
||||
Tel: user.Tel,
|
||||
Email: user.Email,
|
||||
VerifiedEmail: user.VerifiedEmail,
|
||||
VerifiedMobile: user.VerifiedMobile,
|
||||
Remark: user.Remark,
|
||||
IsOn: user.IsOn,
|
||||
CreatedAt: int64(user.CreatedAt),
|
||||
RegisteredIP: user.RegisteredIP,
|
||||
IsVerified: user.IsVerified,
|
||||
IsRejected: user.IsRejected,
|
||||
RejectReason: user.RejectReason,
|
||||
NodeCluster: pbCluster,
|
||||
IsIndividualIdentified: isIndividualIdentified,
|
||||
IsEnterpriseIdentified: isEnterpriseIdentified,
|
||||
BandwidthAlgo: user.BandwidthAlgo,
|
||||
OtpLogin: pbOtpAuth,
|
||||
Lang: user.Lang,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CheckUserUsername 检查用户名是否存在
|
||||
@@ -312,11 +315,14 @@ func (this *UserService) LoginUser(ctx context.Context, req *pb.LoginUserRequest
|
||||
var tx = this.NullTx()
|
||||
|
||||
// 邮箱登录
|
||||
var registerConfig *userconfigs.UserRegisterConfig
|
||||
if strings.Contains(req.Username, "@") {
|
||||
// 是否允许
|
||||
registerConfig, err := models.SharedSysSettingDAO.ReadUserRegisterConfig(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if registerConfig == nil {
|
||||
registerConfig, err = models.SharedSysSettingDAO.ReadUserRegisterConfig(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if registerConfig != nil && registerConfig.EmailVerification.CanLogin {
|
||||
userId, err := models.SharedUserDAO.CheckUserEmailPassword(tx, req.Username, req.Password)
|
||||
@@ -332,6 +338,29 @@ func (this *UserService) LoginUser(ctx context.Context, req *pb.LoginUserRequest
|
||||
}
|
||||
}
|
||||
|
||||
// 手机号登录
|
||||
if utils.IsValidMobile(req.Username) {
|
||||
// 是否允许
|
||||
if registerConfig == nil {
|
||||
registerConfig, err = models.SharedSysSettingDAO.ReadUserRegisterConfig(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if registerConfig != nil && registerConfig.MobileVerification.CanLogin {
|
||||
userId, err := models.SharedUserDAO.CheckUserMobilePassword(tx, req.Username, req.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userId > 0 {
|
||||
return &pb.LoginUserResponse{
|
||||
UserId: userId,
|
||||
IsOk: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 用户名登录
|
||||
userId, err := models.SharedUserDAO.CheckUserPassword(tx, req.Username, req.Password)
|
||||
if err != nil {
|
||||
@@ -840,6 +869,28 @@ func (this *UserService) CheckUserEmail(ctx context.Context, req *pb.CheckUserEm
|
||||
return &pb.CheckUserEmailResponse{Exists: false}, nil
|
||||
}
|
||||
|
||||
// CheckUserMobile 检查手机号码是否被使用
|
||||
func (this *UserService) CheckUserMobile(ctx context.Context, req *pb.CheckUserMobileRequest) (*pb.CheckUserMobileResponse, error) {
|
||||
userId, err := this.ValidateUserNode(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(req.Mobile) == 0 {
|
||||
return nil, errors.New("'mobile' required")
|
||||
}
|
||||
|
||||
var tx = this.NullTx()
|
||||
mobileOwnerUserId, err := models.SharedUserDAO.FindUserIdWithVerifiedMobile(tx, req.Mobile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mobileOwnerUserId > 0 && userId != mobileOwnerUserId {
|
||||
return &pb.CheckUserMobileResponse{Exists: true}, nil
|
||||
}
|
||||
return &pb.CheckUserMobileResponse{Exists: false}, nil
|
||||
}
|
||||
|
||||
// FindUserVerifiedEmailWithUsername 根据用户名查询用户绑定的邮箱
|
||||
func (this *UserService) FindUserVerifiedEmailWithUsername(ctx context.Context, req *pb.FindUserVerifiedEmailWithUsernameRequest) (*pb.FindUserVerifiedEmailWithUsernameResponse, error) {
|
||||
_, err := this.ValidateUserNode(ctx, false)
|
||||
|
||||
12
internal/utils/mobile.go
Normal file
12
internal/utils/mobile.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package utils
|
||||
|
||||
import "regexp"
|
||||
|
||||
var mobileRegex = regexp.MustCompile(`^1\d{10}$`)
|
||||
|
||||
// IsValidMobile validate mobile number
|
||||
func IsValidMobile(mobile string) bool {
|
||||
return mobileRegex.MatchString(mobile)
|
||||
}
|
||||
17
internal/utils/mobile_test.go
Normal file
17
internal/utils/mobile_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsValidMobile(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
a.IsFalse(utils.IsValidMobile("138"))
|
||||
a.IsFalse(utils.IsValidMobile("1382222"))
|
||||
a.IsFalse(utils.IsValidMobile("1381234567890"))
|
||||
a.IsTrue(utils.IsValidMobile("13812345678"))
|
||||
}
|
||||
Reference in New Issue
Block a user