Selaa lähdekoodia

增加-数据结构

userxzz 9 kuukautta sitten
vanhempi
commit
cebf0c447d

+ 1 - 1
game/game_cluster/internal/constant/constant.go

@@ -29,7 +29,7 @@ const (
 	CNamePlayerDailyRecord     = "playerDailyRecord"
 	CNameCashOutRecord         = "cashOutRecord"
 	CNamePlayerLevelStat       = "playerLevelStat"
-	CNameServerLoadStat        = "serverLoadStat "
+	CNameServerLoadStat        = "serverLoadStat"
 	CNamePlayerCountryByIPStat = "playerCountryStat"
 )
 

+ 182 - 0
game/game_cluster/nodes/webadmin/controller/admin.go

@@ -0,0 +1,182 @@
+package controller
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/mhaya/game/game_cluster/nodes/webadmin/entity"
+	"github.com/mhaya/game/game_cluster/nodes/webadmin/service"
+)
+
+type Admin struct {
+	sev *service.Admin
+}
+
+func NewAdmin() *Admin {
+	return &Admin{
+		sev: service.NewAdmin(),
+	}
+}
+
+// Login login
+func (a *Admin) Login(ctx *gin.Context) {
+	var req entity.AdminLoginReq
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	resp, err := a.sev.Login(ctx, req.Username, req.Password)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+		"data": resp,
+	})
+}
+
+// Add 添加管理员
+func (a *Admin) Add(ctx *gin.Context) {
+	var req entity.AdminAddReq
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	err := a.sev.Add(ctx, req.Username, req.Password, req.RealName, req.Pid, req.RoleId, req.Status)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+	})
+}
+
+// ChangePassword 修改密码
+func (a *Admin) ChangePassword(ctx *gin.Context) {
+	var req entity.AdminChangePasswordReq
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	err := a.sev.ChangePassword(ctx, req.Username, req.Password)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+	})
+}
+
+// Del 删除管理员
+func (a *Admin) Del(ctx *gin.Context) {
+	var req entity.AdminDelReq
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	err := a.sev.Delete(ctx, req.Username)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+	})
+}
+
+// FindAll 获取所有管理员
+func (a *Admin) FindAll(ctx *gin.Context) {
+	var req entity.AdminFindAllReq
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+	}
+	resp, err := a.sev.FindAll(ctx, req.Page, req.Size, req.Username)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+		"data": resp,
+	})
+}
+
+func (a *Admin) UpdateStatus(ctx *gin.Context) {
+	var req entity.AdminUpdateStatusReq
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+	}
+	err := a.sev.UpdateStatus(ctx, req.Username, req.Status)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+	})
+
+}
+
+// GetServerStatus
+
+func (a *Admin) GetServerStatus(ctx *gin.Context) {
+	resp, err := a.sev.GetServerStatus(ctx)
+	if err != nil {
+		ctx.JSON(200, gin.H{
+			"code": 400,
+			"msg":  err.Error(),
+		})
+		return
+	}
+	ctx.JSON(http.StatusOK, gin.H{
+		"code": 200,
+		"msg":  "success",
+		"data": resp,
+	})
+
+}

+ 41 - 0
game/game_cluster/nodes/webadmin/entity/admin.go

@@ -0,0 +1,41 @@
+package entity
+
+// AdminResp \game\game_cluster\nodes\webadmin\entity\admin.go
+type AdminResp struct {
+	ToKen string `json:"token"`
+}
+type AdminLoginReq struct {
+	Username string `json:"username"`
+	Password string `json:"password"`
+}
+type AdminAddReq struct {
+	Password string `json:"password"`
+	RealName string `json:"real_name"`
+	Username string `json:"username"`
+	Pid      int64  `json:"pid"`
+	RoleId   int64  `json:"role_id"`
+	Status   int    `json:"status"`
+}
+
+// Admin \game\game_cluster\nodes\webadmin\entity\admin.go
+type AdminChangePasswordReq struct {
+	Password string `json:"
+
+"`
+	Username string `json:"username"`
+}
+
+type AdminDelReq struct {
+	Username string `json:"username"`
+}
+
+type AdminFindAllReq struct {
+	Page     int    `json:"page"`
+	Size     int    `json:"size"`
+	Username string `json:"username"`
+}
+
+type AdminUpdateStatusReq struct {
+	Username string `json:"username"`
+	Status   int    `json:"status"`
+}

+ 9 - 9
game/game_cluster/nodes/webadmin/entity/user_withdrawal.go

@@ -2,15 +2,15 @@ package entity
 
 // UserWithdrawalResp \game\game_cluster\nodes\webadmin\entity\user_withdrawal.go
 type UserWithdrawalResp struct {
-	UserName    string `json:"user_name" bson:"userName"`        // 用户ID
-	NickName    string `json:"nick_name" bson:"nickName"`        // 昵称
-	Status      int    `json:"status" bson:"status"`             // 0:未审核 1:审核通过 2:审核失败
-	Amount      int    `json:"amount" bson:"amount"`             // 提现金额
-	AfterAmount int    `json:"after_amount" bson:"after_amount"` // 提现后金额
-	Type        int    `json:"type" bson:"type"`                 // 货币内型
-	Address     string `json:"address" bson:"address"`           // 地址
-	CreateAt    int64  `json:"createAt" bson:"createAt"`         // 提现时间
-	UpdateAt    int64  `json:"updateAt" bson:"updateAt"`
+	UserName    string      `json:"user_name" bson:"userName"`        // 用户ID
+	NickName    string      `json:"nick_name" bson:"nickName"`        // 昵称
+	Status      interface{} `json:"status" bson:"status"`             // 0:未审核 1:审核通过 2:审核失败
+	Amount      int         `json:"amount" bson:"amount"`             // 提现金额
+	AfterAmount int         `json:"after_amount" bson:"after_amount"` // 提现后金额
+	Type        interface{} `json:"type" bson:"type"`                 // 货币内型
+	Address     string      `json:"address" bson:"address"`           // 地址
+	CreateAt    interface{} `json:"createAt" bson:"createAt"`         // 提现时间
+	UpdateAt    interface{} `json:"updateAt" bson:"updateAt"`
 }
 
 type UserWithdrawalReq struct {

+ 67 - 0
game/game_cluster/nodes/webadmin/model/admin.go

@@ -0,0 +1,67 @@
+package model
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"log"
+	"math/rand"
+)
+
+type Admin struct {
+	Id            int64  `json:"id" bson:"id"`                           // 自增ID
+	Username      string `json:"username" bson:"username"`               // 用户名
+	Password      string `json:"password" bson:"password"`               // 密码
+	RealName      string `json:"real_name" bson:"real_name"`             // 真实姓名
+	Pid           int64  `json:"pid" bson:"pid"`                         // 父级ID
+	RoleId        int64  `json:"role_id" bson:"role_id"`                 // 角色ID
+	Status        int    `json:"status" bson:"status"`                   // 状态 0:禁用 1:启用
+	ManagerAuth   int8   `json:"manager_auth" bson:"manager_auth"`       // 管理员权限
+	LastLoginIp   string `json:"last_login_ip" bson:"last_login_ip"`     // 最后登录IP
+	LastLoginTime int64  `json:"last_login_time" bson:"last_login_time"` // 最后登录时间
+	CreatedAt     int64  `json:"created_at" bson:"created_at"`
+	UpdatedAt     int64  `json:"updated_at" bson:"updated_at"`
+	DeletedAt     int64  `json:"deleted_at" bson:"deleted_at"`
+}
+
+func (a *Admin) TableName() string {
+	return "admin"
+}
+
+const tokenLength = 32 // 定义token长度
+
+// GenerateToken 生成一个安全的token字符串
+func (a *Admin) GenerateToken() (string, error) {
+	key := make([]byte, 32) // AES-256 key length
+	_, err := rand.Read(key)
+	if err != nil {
+		log.Printf("Failed to generate random key: %s", err)
+		return "", err
+	}
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		log.Printf("Failed to create AES cipher: %s", err)
+		return "", err
+	}
+
+	token := make([]byte, tokenLength)
+	gcm, err := cipher.NewGCM(block)
+	if err != nil {
+		log.Printf("Failed to create GCM: %s", err)
+		return "", err
+	}
+
+	nonce := make([]byte, gcm.NonceSize())
+	_, err = rand.Read(nonce)
+	if err != nil {
+		log.Printf("Failed to generate random nonce: %s", err)
+		return "", err
+	}
+	token = gcm.Seal(nil, nonce, token, nil)
+	return base64.URLEncoding.EncodeToString(token), nil
+}
+
+func (a *Admin) GetID() int64 {
+	return a.Id
+}

+ 32 - 0
game/game_cluster/nodes/webadmin/router/middleware.go

@@ -0,0 +1,32 @@
+package router
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/mhaya/game/game_cluster/internal/mdb"
+)
+
+func Auth() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		tokenString := c.GetHeader("Token")
+		if tokenString == "" {
+			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
+				"msg": "token is empty",
+			})
+			return
+		}
+		result, err := mdb.RDB.Get(context.Background(), tokenString).Result()
+		if err != nil {
+			return
+		}
+		if result == "" {
+			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
+				"msg": "token is invalid",
+			})
+			return
+		}
+		c.Next()
+	}
+}

+ 11 - 9
game/game_cluster/nodes/webadmin/router/router.go

@@ -1,8 +1,6 @@
 package router
 
 import (
-	"net/http"
-
 	"github.com/gin-gonic/gin"
 	mhayaGin "github.com/mhaya/components/gin"
 	cfacade "github.com/mhaya/facade"
@@ -31,22 +29,26 @@ func (c *Controller) CreateActor(id string, handler cfacade.IActorHandler) {
 
 // SetRouter 设置路由
 func (c *Controller) SetRouter() {
-	group := c.Group("/")
-	u := group.Group("/v1/admin")
-	c.InitApiRouter(u)
+	u := c.Group("/v1")
+	uu := u.Group("/admin")
+	uu.POST("/user/login", controller.NewAdmin().Login)
+	c.InitApiRouter(uu)
 }
 
 func (c *Controller) InitApiRouter(u *gin.RouterGroup) {
-	u.GET("/hello", func(context *gin.Context) {
-		context.JSON(http.StatusOK, "")
-	})
+	u.Use(Auth())
 	u.POST("/user/log/daily", controller.NewSynthesis().FindUserLogDaily)
 	u.POST("/user/retention", controller.NewSynthesis().FindUserRetention)
 	u.POST("/user/country", controller.NewSynthesis().FindUserCountryCount)
 	u.POST("/user/withdrawal", controller.NewSynthesis().FindWithdrawal)
 	u.POST("/user/withdrawal/status", controller.NewSynthesis().WithdrawalStatus)
 	u.POST("/user/level", controller.NewSynthesis().FindUserLevel)
-
+	u.POST("/user/changePassword", controller.NewAdmin().ChangePassword)
+	u.POST("/user/add", controller.NewAdmin().Add)
+	u.POST("/user/del", controller.NewAdmin().Del)
+	u.POST("/user/find", controller.NewAdmin().FindAll)
+	u.POST("/user/update", controller.NewAdmin().UpdateStatus)
+	u.POST("/user/server_status", controller.NewAdmin().GetServerStatus)
 }
 
 // func (c *Controller) InitMdb() {

+ 289 - 0
game/game_cluster/nodes/webadmin/service/admin.go

@@ -0,0 +1,289 @@
+package service
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"log"
+	"regexp"
+	"strings"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/mhaya/game/game_cluster/internal/constant"
+	"github.com/mhaya/game/game_cluster/internal/mdb"
+	"github.com/mhaya/game/game_cluster/internal/mdb/models"
+	"github.com/mhaya/game/game_cluster/nodes/webadmin/entity"
+	"github.com/mhaya/game/game_cluster/nodes/webadmin/model"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"golang.org/x/crypto/bcrypt"
+)
+
+type Admin struct {
+	db *mongo.Database
+}
+
+func NewAdmin() *Admin {
+	return &Admin{
+		db: mdb.MDB,
+	}
+}
+
+func (a *Admin) GetDB() *mongo.Database {
+	return a.db
+}
+
+func (a *Admin) GetDBName() string {
+	return "admin"
+}
+func CheckPasswordHash(password, hash string) bool {
+	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
+	return err == nil
+}
+
+// HashPassword 加密密码
+func HashPassword(password string) (string, error) {
+	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
+	return string(bytes), err
+}
+
+// Login 登录
+func (a *Admin) Login(ctx *gin.Context, username string, password string) (*entity.AdminResp, error) {
+	log.Printf("Attempting login for user: %s", username)
+	user, err := a.QueryUserByUsername(ctx, username)
+	if err != nil {
+		log.Printf("Failed to query user: %s", err)
+		return nil, fmt.Errorf("failed to query user: %s", err)
+	}
+	// 判断用户状态
+	if user.Status == 0 {
+		log.Println("User is disabled")
+		return nil, errors.New("user is disabled")
+	}
+	// 判断密码
+	if !CheckPasswordHash(password, user.Password) {
+		log.Println("Invalid username or password")
+		return nil, errors.New("invalid username or password")
+	}
+	// 创建token
+	generateToken, err := user.GenerateToken()
+	if err != nil {
+		return nil, err
+	}
+	// 保存token 到 redis 中 过期时间为1天
+	err = mdb.RDB.Set(ctx, generateToken, user.Username, 24*time.Hour).Err()
+	if err != nil {
+		return nil, err
+	}
+	// 更新用户登录时间
+	_, err = mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"last_login_time": time.Now().Unix()}})
+	log.Printf("Login successful for user: %s", username)
+	if err != nil {
+		return nil, err
+	}
+	// 更新用户IP
+	ip := ctx.ClientIP()
+	_, err = mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"last_login_ip": ip}})
+
+	// 返回用户信息
+	log.Printf("Returning user: %s", username)
+	return &entity.AdminResp{
+		ToKen: generateToken,
+	}, nil
+}
+
+// QueryUserByUsername 根据用户名查询用户
+func (a *Admin) QueryUserByUsername(ctx context.Context, username string) (*model.Admin, error) {
+	admin := model.Admin{}
+	err := mdb.MDB.Collection(a.GetDBName()).FindOne(ctx, bson.M{"username": username}).Decode(&admin)
+	if errors.Is(err, mongo.ErrNoDocuments) && username == "admin" {
+		// 如果是admin 登录的话 创建一个初始的admin并且存入数据库
+		if username == "admin" {
+			pwd, err := HashPassword("123456")
+			if err != nil {
+				return nil, err
+			}
+			adminn := &model.Admin{
+				Username:      "admin",
+				Password:      pwd,
+				RealName:      "admin",
+				Pid:           0,
+				RoleId:        0,
+				ManagerAuth:   0,
+				Status:        1,
+				CreatedAt:     0,
+				UpdatedAt:     0,
+				DeletedAt:     0,
+				LastLoginIp:   "",
+				LastLoginTime: 0,
+				Id:            0,
+			}
+			_, err = mdb.MDB.Collection(a.GetDBName()).InsertOne(ctx, adminn)
+			if err != nil {
+				return nil, err
+			}
+			return adminn, nil
+		}
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return &admin, nil
+
+}
+
+// ChangePassword 修改管理员密码
+func (a *Admin) ChangePassword(ctx context.Context, username string, password string) error {
+	// 更新密码
+	password, _ = HashPassword(password)
+	_, err := mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"password": password}})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Add 添加管理员
+func (a *Admin) Add(ctx context.Context, username string, password string, realName string, pid int64, roleId int64, status int) error {
+	// 判断账号是否重复
+	admin := model.Admin{}
+	err := mdb.MDB.Collection(a.GetDBName()).FindOne(ctx, bson.M{"username": username}).Decode(&admin)
+	if errors.Is(err, mongo.ErrNoDocuments) {
+		password, _ = HashPassword(password)
+		_, err := mdb.MDB.Collection(a.GetDBName()).InsertOne(ctx, &model.Admin{
+			Username:      username,
+			Password:      password,
+			RealName:      realName,
+			Pid:           pid,
+			RoleId:        roleId,
+			Status:        status,
+			CreatedAt:     time.Now().Unix(),
+			UpdatedAt:     time.Now().Unix(),
+			DeletedAt:     0,
+			LastLoginIp:   "",
+			LastLoginTime: 0,
+		})
+		if err != nil {
+			return err
+		}
+		return nil
+	}
+	return fmt.Errorf("账号已存在")
+}
+
+// Delete 删除管理员
+func (a *Admin) Delete(ctx context.Context, username string) error {
+	_, err := mdb.MDB.Collection(a.GetDBName()).DeleteOne(ctx, bson.M{"username": username})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// updateStatus
+func (a *Admin) UpdateStatus(ctx context.Context, username string, status int) error {
+	_, err := mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"status": status}})
+	if err != nil {
+		return err
+	}
+	return nil
+
+}
+
+// FindAll 查找所有管理员信息
+func (a *Admin) FindAll(ctx context.Context, page int, pageSize int, username string) ([]*model.Admin, error) {
+	// 日志记录
+	log.Printf("Finding admins with page %d and page size %d, username: %s", page, pageSize, maskUsername(username))
+
+	// 验证参数
+	if page <= 0 {
+		page = 1
+	}
+	if pageSize <= 0 {
+		pageSize = 10
+	}
+
+	// 构建查询条件
+	filter := bson.M{}
+	if username != "" {
+		filter["username"] = bson.M{"$regex": escapeRegex(username), "$options": "i"}
+	}
+
+	// 设置分页选项
+	skip := (page - 1) * pageSize
+	limit := pageSize
+	findOptions := options.Find().SetSkip(int64(skip)).SetLimit(int64(limit))
+
+	// 执行查询
+	cursor, err := mdb.MDB.Collection("admin").Find(ctx, filter, findOptions)
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		if closeErr := cursor.Close(ctx); closeErr != nil {
+			log.Printf("Error closing cursor: %v", closeErr)
+		}
+	}()
+
+	// 解析结果
+	admins := make([]*model.Admin, 0)
+	for cursor.Next(ctx) {
+		var admin model.Admin
+		err := cursor.Decode(&admin)
+		if err != nil {
+			return nil, err
+		}
+		admins = append(admins, &admin)
+	}
+
+	if err := cursor.Err(); err != nil {
+		return nil, err
+	}
+
+	return admins, nil
+}
+
+// GetServerStatus 获取服务器状态
+func (a *Admin) GetServerStatus(ctx context.Context) ([]*models.PlayerServerLoadStat, error) {
+	// 执行查询
+	cursor, err := mdb.MDB.Collection(constant.CNameServerLoadStat).Find(ctx, bson.M{})
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		if closeErr := cursor.Close(ctx); closeErr != nil {
+			log.Printf("Error closing cursor: %v", closeErr)
+		}
+	}()
+	// 解析结果
+	admins := make([]*models.PlayerServerLoadStat, 0)
+	for cursor.Next(ctx) {
+		var admin models.PlayerServerLoadStat
+		err := cursor.Decode(&admin)
+		if err != nil {
+			return nil, err
+		}
+		admins = append(admins, &admin)
+	}
+
+	if err := cursor.Err(); err != nil {
+		return nil, err
+	}
+	return admins, nil
+}
+
+// 辅助函数:对 username 进行脱敏处理
+func maskUsername(username string) string {
+	if username == "" {
+		return ""
+	}
+	return strings.Repeat("*", len(username))
+}
+
+// 辅助函数:对正则表达式进行转义
+func escapeRegex(s string) string {
+	return regexp.QuoteMeta(s)
+}

+ 2 - 1
game/game_cluster/nodes/webadmin/service/synthesis.go

@@ -114,7 +114,7 @@ func (s *Synthesis) FindMDBUserLogDaily(req *entity.UserLogDailyReq) ([]entity.U
 func (s *Synthesis) FindWithdrawal(req *entity.UserWithdrawalReq) ([]*entity.UserWithdrawalResp, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
 	defer cancel()
-	collection := mdb.MDB.Collection("cashOutRecord ")
+	collection := mdb.MDB.Collection(constant.CNameCashOutRecord)
 
 	// 构建过滤器
 	filter := bson.M{}
@@ -199,6 +199,7 @@ func (s *Synthesis) FindUserCountryCount() ([]*entity.UserCountryResp, error) {
 			}},
 		},
 		{
+
 			{Key: "$unwind", Value: "$playerRegisterCountry"},
 		},
 		{