Skip to content

Commit

Permalink
实现了用户注册逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
showurl committed Jun 14, 2023
1 parent 40d040c commit dd9a299
Show file tree
Hide file tree
Showing 20 changed files with 318 additions and 11 deletions.
1 change: 1 addition & 0 deletions app/gateway/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Config struct {
}
RpcClientConf struct {
Dispatch zrpc.RpcClientConf
User zrpc.RpcClientConf
Third zrpc.RpcClientConf
}
RedisConf redis.RedisConf
Expand Down
10 changes: 10 additions & 0 deletions app/gateway/internal/handler/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ func SetupRoutes(svcCtx *svc.ServiceContext, engine *gin.Engine) {
// GatewayKeepAliveReq GatewayKeepAliveResp
AddWsRoute("/v1/gateway/keepAlive", KeepAliveHandler(svcCtx))
}
// user api
{
// UserRegisterReq UserRegisterResp
AddUnifiedRoute("/v1/user/userRegister", Route[*pb.UserRegisterReq, *pb.UserRegisterResp]{
NewRequest: func() *pb.UserRegisterReq {
return &pb.UserRegisterReq{}
},
Do: svcCtx.UserService.UserRegister,
})
}
// http
{
apiGroup := engine.Group("/api")
Expand Down
8 changes: 5 additions & 3 deletions app/gateway/internal/logic/x_wsManager.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ func (w *wsManager) RemoveSubscriber(header *pb.RequestHeader, id int64, closeCo
}
w.wsConnectionMap.Delete(header.UserId, id)
go func() {
_, e := w.svcCtx.DispatchService.DispatchOfflineCallback(connection.Ctx, &pb.DispatchOfflineCallbackReq{Header: header})
if e != nil {
logx.Errorf("DispatchOfflineCallback error: %s", e.Error())
if ok {
_, e := w.svcCtx.DispatchService.DispatchOfflineCallback(connection.Ctx, &pb.DispatchOfflineCallbackReq{Header: header})
if e != nil {
logx.Errorf("DispatchOfflineCallback error: %s", e.Error())
}
}
}()
return nil
Expand Down
7 changes: 7 additions & 0 deletions app/gateway/internal/svc/serviceContext.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/cherish-chat/xxim-server/app/dispatch/dispatchservice"
"github.com/cherish-chat/xxim-server/app/gateway/gatewayservice"
"github.com/cherish-chat/xxim-server/app/gateway/internal/config"
"github.com/cherish-chat/xxim-server/app/user/userservice"
"github.com/cherish-chat/xxim-server/common/xcache"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/zrpc"
Expand All @@ -15,6 +16,7 @@ type ServiceContext struct {
Config config.Config
gatewayService gatewayservice.GatewayService
DispatchService dispatchservice.DispatchService
UserService userservice.UserService
Redis *redis.Redis
}

Expand All @@ -26,6 +28,11 @@ func NewServiceContext(c config.Config) *ServiceContext {
zrpc.WithNonBlock(),
zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond),
)),
UserService: userservice.NewUserService(zrpc.MustNewClient(
c.RpcClientConf.User,
zrpc.WithNonBlock(),
zrpc.WithTimeout(time.Duration(c.Timeout)*time.Millisecond),
)),
Redis: xcache.MustNewRedis(c.RedisConf),
}
return s
Expand Down
2 changes: 1 addition & 1 deletion app/third/internal/logic/captchaVerifyLogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func NewCaptchaVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Cap
func (l *CaptchaVerifyLogic) CaptchaVerify(in *pb.CaptchaVerifyReq) (*pb.CaptchaVerifyResp, error) {
// todo: add your logic here and delete this line

return &pb.CaptchaVerifyResp{}, nil
return &pb.CaptchaVerifyResp{Success: true}, nil
}
2 changes: 1 addition & 1 deletion app/third/internal/logic/emailCodeVerifyLogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func NewEmailCodeVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *E
func (l *EmailCodeVerifyLogic) EmailCodeVerify(in *pb.EmailCodeVerifyReq) (*pb.EmailCodeVerifyResp, error) {
// todo: add your logic here and delete this line

return &pb.EmailCodeVerifyResp{}, nil
return &pb.EmailCodeVerifyResp{Success: true}, nil
}
2 changes: 1 addition & 1 deletion app/third/internal/logic/smsCodeVerifyLogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func NewSmsCodeVerifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Sms
func (l *SmsCodeVerifyLogic) SmsCodeVerify(in *pb.SmsCodeVerifyReq) (*pb.SmsCodeVerifyResp, error) {
// todo: add your logic here and delete this line

return &pb.SmsCodeVerifyResp{}, nil
return &pb.SmsCodeVerifyResp{Success: true}, nil
}
16 changes: 15 additions & 1 deletion app/user/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,21 @@ type Config struct {
MongoCollection xmgo.MongoCollectionConf
}
Account struct {
Register struct {
//UsernameUnique 用户名是否唯一
UsernameUnique bool `json:",optional"`
//UserRegex 用户名正则
UserRegex string `json:",optional"`
//手机号是否唯一
PhoneUnique bool `json:",optional"`
//PhoneRegex 手机号正则
PhoneRegex string `json:",optional"`
//PhoneCode 国家区号
PhoneCode []string `json:",optional"`
//邮箱是否唯一
EmailUnique bool `json:",optional"`
//EmailRegex 邮箱正则
EmailRegex string `json:",optional"`
Register struct {
//AllowPlatform 可以接受的platform
//const (
// Platform_IOS Platform = 0 // ios
Expand Down
125 changes: 125 additions & 0 deletions app/user/internal/logic/userRegisterLogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
"github.com/cherish-chat/xxim-server/common/i18n"
"github.com/cherish-chat/xxim-server/common/pb"
"github.com/cherish-chat/xxim-server/common/utils"
"github.com/cherish-chat/xxim-server/common/xcache"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"time"

"github.com/zeromicro/go-zero/core/logx"
Expand Down Expand Up @@ -50,6 +52,51 @@ func (l *UserRegisterLogic) UserRegister(in *pb.UserRegisterReq) (*pb.UserRegist
}

//是否必填password
username, ok := in.AccountMap[pb.AccountTypeUsername]
if !ok {
if l.svcCtx.Config.Account.Register.RequirePassword {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "username_required")),
}, nil
}
} else {
user.AccountMap[pb.AccountTypeUsername] = username
if l.svcCtx.Config.Account.UserRegex != "" {
if !utils.Regex.Match(l.svcCtx.Config.Account.UserRegex, username) {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "username_format_error")),
}, nil
}
}
}
if l.svcCtx.Config.Account.UsernameUnique {
//用户名上锁
ok, err := xcache.Lock.Lock(l.ctx, l.svcCtx.Redis, xcache.UserUsernameLockKey(username), 5)
if err != nil || !ok {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "username_lock_error")),
}, nil
}
defer xcache.Lock.Unlock(l.ctx, l.svcCtx.Redis, xcache.UserUsernameLockKey(username))
//检查用户名是否已存在
found := &usermodel.User{}
err = l.svcCtx.UserCollection.Find(l.ctx, bson.M{
"accountMap." + pb.AccountTypeUsername: username,
}).One(found)
if err != nil {
if err != mongo.ErrNoDocuments {
l.Errorf("UserRegisterLogic.UserRegister l.svcCtx.UserCollection.Find error: %v", err)
return nil, err
} else {
// 没问题
}
} else {
// 已存在
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "username_already_exists")),
}, nil
}
}
passwordSalt, ok := in.AccountMap[pb.AccountTypePasswordSalt]
if !ok {
if l.svcCtx.Config.Account.Register.RequirePassword {
Expand Down Expand Up @@ -92,6 +139,20 @@ func (l *UserRegisterLogic) UserRegister(in *pb.UserRegisterReq) (*pb.UserRegist
} else {
user.AccountMap[pb.AccountTypePhoneCode] = phoneCode
}
if phone != "" && phoneCode != "" {
if l.svcCtx.Config.Account.PhoneRegex != "" {
if !utils.Regex.Match(l.svcCtx.Config.Account.PhoneRegex, phone) {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "phone_format_error")),
}, nil
}
}
if !utils.AnyInSlice[string](phoneCode, l.svcCtx.Config.Account.PhoneCode) {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "phone_code_error")),
}, nil
}
}
smsCode, ok := in.VerifyMap[pb.VerifyTypeSmsCode]
if !ok {
if l.svcCtx.Config.Account.Register.RequireBindPhone {
Expand Down Expand Up @@ -121,6 +182,35 @@ func (l *UserRegisterLogic) UserRegister(in *pb.UserRegisterReq) (*pb.UserRegist
}, nil
}
}
if l.svcCtx.Config.Account.PhoneUnique {
//手机号上锁
ok, err := xcache.Lock.Lock(l.ctx, l.svcCtx.Redis, xcache.UserPhoneLockKey(phone, phoneCode), 5)
if err != nil || !ok {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "phone_lock_error")),
}, nil
}
defer xcache.Lock.Unlock(l.ctx, l.svcCtx.Redis, xcache.UserPhoneLockKey(phone, phoneCode))
//检查用户名是否已存在
found := &usermodel.User{}
err = l.svcCtx.UserCollection.Find(l.ctx, bson.M{
"accountMap." + pb.AccountTypePhone: phone,
"accountMap." + pb.AccountTypePhoneCode: phoneCode,
}).One(found)
if err != nil {
if err != mongo.ErrNoDocuments {
l.Errorf("UserRegisterLogic.UserRegister l.svcCtx.UserCollection.Find error: %v", err)
return nil, err
} else {
// 没问题
}
} else {
// 已存在
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "phone_already_exists")),
}, nil
}
}

//是否必填邮箱
email, ok := in.AccountMap[pb.AccountTypeEmail]
Expand All @@ -141,6 +231,13 @@ func (l *UserRegisterLogic) UserRegister(in *pb.UserRegisterReq) (*pb.UserRegist
}, nil
}
} else {
if l.svcCtx.Config.Account.EmailRegex != "" {
if !utils.Regex.Match(l.svcCtx.Config.Account.EmailRegex, email) {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "email_format_error")),
}, nil
}
}
//验证邮箱验证码
emailVerifyResp, err := l.svcCtx.ThirdService.EmailCodeVerify(l.ctx, &pb.EmailCodeVerifyReq{
Header: in.Header,
Expand All @@ -161,6 +258,34 @@ func (l *UserRegisterLogic) UserRegister(in *pb.UserRegisterReq) (*pb.UserRegist
}, nil
}
}
if l.svcCtx.Config.Account.EmailUnique {
//手机号上锁
ok, err := xcache.Lock.Lock(l.ctx, l.svcCtx.Redis, xcache.UserEmailLockKey(email), 5)
if err != nil || !ok {
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "email_lock_error")),
}, nil
}
defer xcache.Lock.Unlock(l.ctx, l.svcCtx.Redis, xcache.UserEmailLockKey(email))
//检查用户名是否已存在
found := &usermodel.User{}
err = l.svcCtx.UserCollection.Find(l.ctx, bson.M{
"accountMap." + pb.AccountTypeEmail: email,
}).One(found)
if err != nil {
if err != mongo.ErrNoDocuments {
l.Errorf("UserRegisterLogic.UserRegister l.svcCtx.UserCollection.Find error: %v", err)
return nil, err
} else {
// 没问题
}
} else {
// 已存在
return &pb.UserRegisterResp{
Header: i18n.NewToastHeader(pb.ToastActionData_ERROR, i18n.Get(in.Header.Language, "email_already_exists")),
}, nil
}
}

//是否必填昵称
if in.Nickname == nil || *in.Nickname == "" {
Expand Down
10 changes: 7 additions & 3 deletions app/user/usermodel/user.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package usermodel

import (
"github.com/cherish-chat/xxim-server/common/pb"
"github.com/qiniu/qmgo"
opts "github.com/qiniu/qmgo/options"
"github.com/zeromicro/go-zero/core/stores/redis"
Expand All @@ -11,7 +12,7 @@ import (

// User 用户 数据库模型
type User struct {
Id primitive.ObjectID `bson:"_id" json:"-"`
Id primitive.ObjectID `bson:"_id,omitempty" json:"-"`

// 账户信息
// Username 用户名 唯一
Expand Down Expand Up @@ -49,10 +50,13 @@ func (m *User) GetIndexes() []opts.IndexModel {
Key: []string{"-destroyTime"},
IndexOptions: options.Index().SetName("destroyTime"),
}, {
Key: []string{"accountMap.username"},
Key: []string{"accountMap." + pb.AccountTypeUsername},
IndexOptions: options.Index().SetName("username"),
}, {
Key: []string{"accountMap.phone", "accountMap.phoneCountryCode"},
Key: []string{"accountMap." + pb.AccountTypeEmail},
IndexOptions: options.Index().SetName("email"),
}, {
Key: []string{"accountMap." + pb.AccountTypePhone, "accountMap." + pb.AccountTypePhoneCode},
IndexOptions: options.Index().SetName("phone"),
}}
}
Expand Down
1 change: 1 addition & 0 deletions common/pb/user.pb.xx.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pb

const (
AccountTypeUsername = "username"
AccountTypePassword = "password"
AccountTypePasswordSalt = "passwordSalt"
AccountTypePhone = "phone"
Expand Down
4 changes: 4 additions & 0 deletions common/pb/xx.implement.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ func (x *GatewayWriteDataToWsReq) SetHeader(header *RequestHeader) {
func (x *GatewayKickWsReq) SetHeader(header *RequestHeader) {
x.Header = header
}

func (x *UserRegisterReq) SetHeader(header *RequestHeader) {
x.Header = header
}
12 changes: 12 additions & 0 deletions common/utils/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ func AnyPtr[T any](v T) *T {
type EnumInSliceType interface {
String() string
}
type AnyInSliceType interface {
string | int8 | int16 | int32 | int64 | int | uint8 | uint16 | uint32 | uint64 | uint | float32 | float64 | bool
}

func EnumInSlice[T EnumInSliceType](v T, slice []T) bool {
for _, item := range slice {
Expand All @@ -17,6 +20,15 @@ func EnumInSlice[T EnumInSliceType](v T, slice []T) bool {
return false
}

func AnyInSlice[T AnyInSliceType](v T, slice []T) bool {
for _, item := range slice {
if item == v {
return true
}
}
return false
}

func AnyString(o any) string {
switch v := o.(type) {
case string:
Expand Down
16 changes: 16 additions & 0 deletions common/utils/regex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package utils

import "regexp"

type xRegex struct {
}

var Regex = &xRegex{}

func (x *xRegex) Match(pattern string, s string) bool {
matched, err := regexp.MatchString(pattern, s)
if err != nil {
return false
}
return matched
}
Loading

0 comments on commit dd9a299

Please sign in to comment.