From 1e2df532d3ee1ed0cf16fea336e93cc3e0663050 Mon Sep 17 00:00:00 2001 From: GoEdgeLab Date: Fri, 17 Nov 2023 11:51:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=94=A8=E6=88=B7=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=89=8B=E6=9C=BA=E5=8F=B7=E7=A0=81=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=92=8C=E7=99=BB=E5=BD=95=EF=BC=88=E5=95=86=E4=B8=9A=E7=89=88?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/db/models/user_dao.go | 40 ++++++- .../db/models/user_mobile_verification_dao.go | 28 +++++ .../user_mobile_verification_dao_test.go | 6 ++ .../models/user_mobile_verification_model.go | 41 +++++++ .../user_mobile_verification_model_ext.go | 1 + internal/db/models/user_model.go | 3 + internal/rpc/services/users/service_user.go | 101 +++++++++++++----- internal/utils/mobile.go | 12 +++ internal/utils/mobile_test.go | 17 +++ 9 files changed, 223 insertions(+), 26 deletions(-) create mode 100644 internal/db/models/user_mobile_verification_dao.go create mode 100644 internal/db/models/user_mobile_verification_dao_test.go create mode 100644 internal/db/models/user_mobile_verification_model.go create mode 100644 internal/db/models/user_mobile_verification_model_ext.go create mode 100644 internal/utils/mobile.go create mode 100644 internal/utils/mobile_test.go diff --git a/internal/db/models/user_dao.go b/internal/db/models/user_dao.go index b975427d..3114a83e 100644 --- a/internal/db/models/user_dao.go +++ b/internal/db/models/user_dao.go @@ -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). diff --git a/internal/db/models/user_mobile_verification_dao.go b/internal/db/models/user_mobile_verification_dao.go new file mode 100644 index 00000000..c4a3adf4 --- /dev/null +++ b/internal/db/models/user_mobile_verification_dao.go @@ -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() + }) +} diff --git a/internal/db/models/user_mobile_verification_dao_test.go b/internal/db/models/user_mobile_verification_dao_test.go new file mode 100644 index 00000000..6595eab6 --- /dev/null +++ b/internal/db/models/user_mobile_verification_dao_test.go @@ -0,0 +1,6 @@ +package models_test + +import ( + _ "github.com/go-sql-driver/mysql" + _ "github.com/iwind/TeaGo/bootstrap" +) diff --git a/internal/db/models/user_mobile_verification_model.go b/internal/db/models/user_mobile_verification_model.go new file mode 100644 index 00000000..2b0699f8 --- /dev/null +++ b/internal/db/models/user_mobile_verification_model.go @@ -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{} +} diff --git a/internal/db/models/user_mobile_verification_model_ext.go b/internal/db/models/user_mobile_verification_model_ext.go new file mode 100644 index 00000000..2640e7f9 --- /dev/null +++ b/internal/db/models/user_mobile_verification_model_ext.go @@ -0,0 +1 @@ +package models diff --git a/internal/db/models/user_model.go b/internal/db/models/user_model.go index 8f8b1920..d2591006 100644 --- a/internal/db/models/user_model.go +++ b/internal/db/models/user_model.go @@ -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 // 邮箱地址 diff --git a/internal/rpc/services/users/service_user.go b/internal/rpc/services/users/service_user.go index d12575a7..80d8cdde 100644 --- a/internal/rpc/services/users/service_user.go +++ b/internal/rpc/services/users/service_user.go @@ -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) diff --git a/internal/utils/mobile.go b/internal/utils/mobile.go new file mode 100644 index 00000000..3a9d61da --- /dev/null +++ b/internal/utils/mobile.go @@ -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) +} diff --git a/internal/utils/mobile_test.go b/internal/utils/mobile_test.go new file mode 100644 index 00000000..add4bfd4 --- /dev/null +++ b/internal/utils/mobile_test.go @@ -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")) +}