package logic import ( "ecs/proto" "ecs/proto/pb" "errors" "github.com/oylshe1314/framework/client/db" "github.com/oylshe1314/framework/log" "github.com/oylshe1314/framework/server" "github.com/oylshe1314/framework/util" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "golang.org/x/crypto/bcrypt" "sync" ) type manager struct { server server.Server } func (this *manager) Logger() log.Logger { return this.server.Logger() } type UserManager struct { manager mongoClient db.MongoClient locker sync.RWMutex users map[string]*User tokens map[uint64]string } func NewUserManager(svr server.Server, mongoClient db.MongoClient) *UserManager { return &UserManager{ manager: manager{ server: svr, }, mongoClient: mongoClient, users: map[string]*User{}, tokens: map[uint64]string{}, } } func (this *UserManager) Init() error { return nil } func (this *UserManager) Close() error { return nil } func (this *UserManager) saveUser(user *User) error { _, err := this.mongoClient.Collection(TableUser).InsertOne(this.mongoClient.Context(), user) return err } func (this *UserManager) queryUser(filter bson.M) (*User, error) { var user = new(User) var err = this.mongoClient.Collection(TableUser).FindOne(this.mongoClient.Context(), filter).Decode(user) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { return nil, nil } return nil, err } return user, nil } func (this *UserManager) updateUser(id string, set bson.M) error { var _, err = this.mongoClient.Collection(TableUser).UpdateByID(this.mongoClient.Context(), id, bson.M{"$set": set}) return err } func (this *UserManager) signUp(origin *proto.UserOrigin, auth *proto.UserAuth, thirdInfo map[string]interface{}) (*User, error) { counter, err := this.mongoClient.Counter("user_id", 1) if err != nil { return nil, err } var uid = util.EncryptUid(counter) if len(auth.Password) > 0 { password, err := bcrypt.GenerateFromPassword([]byte(auth.Password), bcrypt.DefaultCost) if err != nil { this.Logger().Error(err) return nil, err } auth.Password = string(password) } var user = &User{ Id: UserKey(origin.Channel, auth.Username), UserId: uid, Platform: origin.Platform, Channel: origin.Channel, Username: auth.Username, Password: auth.Password, CreateTime: util.NowUnix(), ThirdInfo: thirdInfo, } err = this.saveUser(user) if err != nil { return nil, err } return user, nil } func (this *UserManager) SignUp(origin *proto.UserOrigin, auth *proto.UserAuth) (*User, error) { if origin == nil { this.Logger().Error("Parameter error, UserOrigin == nil") return nil, proto.ErrParameterError } if auth == nil { this.Logger().Error("Parameter error, UserAuth == nil") return nil, proto.ErrParameterError } user, err := this.queryUser(bson.M{"_id": UserKey(origin.Channel, auth.Username)}) if err != nil { this.Logger().Error(err) return nil, proto.ErrInternalError } if user != nil { return nil, proto.ErrUsernameExists } user, err = this.signUp(origin, auth, nil) if err != nil { this.Logger().Error(err) return nil, proto.ErrInternalError } return user, nil } func (this *UserManager) Login(origin *proto.UserOrigin, auth *proto.UserAuth, third *proto.UserThird) (*User, string, error) { if origin == nil { this.Logger().Error("Parameter error, UserOrigin == nil") return nil, "", proto.ErrParameterError } //var thirdInfo map[string]any switch pb.Channel(origin.Channel) { case pb.Channel_Internal: if auth == nil { this.Logger().Error("Parameter error, UserAuth == nil") return nil, "", proto.ErrParameterError } //case pb.Channel_WechatMiniGame: // if third == nil { // this.logger.Error("Parameter error, UserThird == nil") // return nil, "", proto.ErrParameterError // } // // ack, err := wechat.Code2Session(third.Token) // if err != nil { // this.logger.Error("Code to session failed, ", err) // return nil, "", proto.ErrUserLoginFailed // } // // if ack.ErrCode != 0 { // this.logger.Error("Third user login failed, msg: ", ack.ErrMsg) // return nil, "", proto.ErrUserLoginFailed // } // // auth = &proto.UserAuth{Username: ack.OpenId} // thirdInfo = map[string]any{"openid": ack.OpenId, "unionid": ack.UnionId, "sessionKey": ack.SessionKey} //case proto.ChannelTapTap: // if third == nil { // this.logger.Error("Parameter error, UserThird == nil") // return nil, "", proto.ErrParameterError // } // // if third.Args == nil || third.Args.TapTap == nil || third.Args.TapTap.MacKey == "" { // return nil, "", proto.ErrParameterError // } // // ack, err := taptap.AccountProfile(third.Token, third.Args.TapTap.MacKey) // if err != nil { // this.logger.Error(err) // return nil, "", proto.ErrUserLoginFailed // } // // if !ack.Success { // return nil, "", proto.ErrUserLoginFailed // } // // auth = &proto.UserAuth{Username: ack.Data.Openid} // thirdInfo = map[string]interface{}{"name": ack.Data.Name, "avatar": ack.Data.Avatar} default: return nil, "", proto.ErrChannelError } user, err := this.queryUser(bson.M{"_id": UserKey(origin.Channel, auth.Username)}) if err != nil { this.Logger().Error(err) return nil, "", proto.ErrInternalError } if user == nil { user, err = this.signUp(origin, auth, nil) if err != nil { this.Logger().Error(err) return nil, "", proto.ErrInternalError } } else { if user.BanLogin && user.BanLoginTime > util.NowUnix() { return nil, "", proto.ErrUserBanLogin } if len(auth.Password) > 0 { err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(auth.Password)) if err != nil { this.Logger().Error(err) return nil, "", proto.ErrPasswordError } } //if thirdInfo != nil { // if !reflect.DeepEqual(user.ThirdInfo, thirdInfo) { // user.ThirdInfo = thirdInfo // err = this.updateUser(user.Id, bson.M{"third_info": user.ThirdInfo}) // if err != nil { // this.Logger().Error(err) // return nil, "", proto.ErrInternalError // } // } //} } var token = util.RandomToken() this.locker.Lock() defer this.locker.Unlock() if expired, ok := this.tokens[user.UserId]; ok { delete(this.users, expired) } this.users[token] = user this.tokens[user.UserId] = token return user, token, nil } func (this *UserManager) TokenVerify(token string, serverId uint32) (*User, error) { this.locker.RLock() var user = this.users[token] this.locker.RUnlock() if user == nil { return nil, nil } if user.RecentServer != serverId { user.RecentServer = serverId var err = this.updateUser(user.Id, bson.M{"recent_server": user.RecentServer}) if err != nil { this.Logger().Error(err) return nil, proto.ErrInternalError } } return user, nil } //func (this *UserManager) Ban(userId string, banDays uint32) error { // user, err := this.queryUser(bson.M{"_id": userId}) // if err != nil { // this.logger.Error("Query user error, ", err) // return proto.RawErrInternalError // } // // if user == nil { // return proto.RawErrUserNotExists // } // // if banDays == 0 { // user.BanLogin = false // user.BanLoginTime = int64(0) // } else { // user.BanLogin = true // user.BanLoginTime = util.TodayBeginTime() + 86400*int64(banDays) // } // // _, err = this.mongoClient.Collection(TableUser).UpdateByID(this.mongoClient.Context(), user.Id, bson.M{"$set": bson.M{"ban_login": user.BanLogin, "ban_login_time": user.BanLoginTime}}) // if err != nil { // this.logger.Error(err) // return proto.RawErrInternalError // } // // if banDays == 0 { // return nil // } // // return nil //} // //func (this *UserManager) ResetPassword(userId string, password string) error { // user, err := this.queryUser(bson.M{"_id": userId}) // if err != nil { // this.logger.Error("Query user error, ", err) // return proto.RawErrInternalError // } // // if user == nil { // return proto.RawErrUserNotExists // } // // user.Password = password // // _, err = this.mongoClient.Collection(TableUser).UpdateByID(this.mongoClient.Context(), user.Id, bson.M{"$set": bson.M{"password": user.Password}}) // if err != nil { // this.logger.Error(err) // return proto.RawErrInternalError // } // // return nil //}