移除login直连数据库,将数据库操作交给db服处理
This commit is contained in:
parent
a2ecd518aa
commit
78283ada73
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AccountNormal = 1 // 正常
|
//AccountNormal = 1 // 正常
|
||||||
AccountFrozen = 2 // 冻结
|
AccountFrozen = 2 // 冻结
|
||||||
AccountBanned = 3 // 封禁
|
AccountBanned = 3 // 封禁
|
||||||
)
|
)
|
||||||
@ -33,7 +33,7 @@ func (u UserAccount) GetId() uint {
|
|||||||
// 玩家登录记录表
|
// 玩家登录记录表
|
||||||
type UserLoginLog struct {
|
type UserLoginLog struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
PlayerID uint `gorm:"index"` // 关联玩家ID
|
UID uint `gorm:"index"` // 关联玩家ID
|
||||||
LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP
|
LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP
|
||||||
LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间
|
LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间
|
||||||
DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式)
|
DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式)
|
||||||
|
@ -10,7 +10,8 @@ enum ErrCode
|
|||||||
LoginUserOrPwdErr = 102; // 帐号或密码错误
|
LoginUserOrPwdErr = 102; // 帐号或密码错误
|
||||||
AccountFrozen = 103; // 帐号已冻结
|
AccountFrozen = 103; // 帐号已冻结
|
||||||
AccountBanned = 104; // 帐号已封禁
|
AccountBanned = 104; // 帐号已封禁
|
||||||
RegisterUserExist = 120; // 已有该帐号,无法注册
|
RegisterUserExist = 110; // 已有该帐号,无法注册
|
||||||
|
VersionTooLow = 115; // 版本太低,无法登陆
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ enum ServiceTypeId
|
|||||||
STI_Gate = 100; // 网关id
|
STI_Gate = 100; // 网关id
|
||||||
STI_Login = 101; // 登陆服
|
STI_Login = 101; // 登陆服
|
||||||
STI_Chat = 102; // 聊天服
|
STI_Chat = 102; // 聊天服
|
||||||
|
STI_DB = 103; // db服
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,7 +30,8 @@ const (
|
|||||||
ErrCode_LoginUserOrPwdErr ErrCode = 102 // 帐号或密码错误
|
ErrCode_LoginUserOrPwdErr ErrCode = 102 // 帐号或密码错误
|
||||||
ErrCode_AccountFrozen ErrCode = 103 // 帐号已冻结
|
ErrCode_AccountFrozen ErrCode = 103 // 帐号已冻结
|
||||||
ErrCode_AccountBanned ErrCode = 104 // 帐号已封禁
|
ErrCode_AccountBanned ErrCode = 104 // 帐号已封禁
|
||||||
ErrCode_RegisterUserExist ErrCode = 120 // 已有该帐号,无法注册
|
ErrCode_RegisterUserExist ErrCode = 110 // 已有该帐号,无法注册
|
||||||
|
ErrCode_VersionTooLow ErrCode = 115 // 版本太低,无法登陆
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for ErrCode.
|
// Enum value maps for ErrCode.
|
||||||
@ -42,7 +43,8 @@ var (
|
|||||||
102: "LoginUserOrPwdErr",
|
102: "LoginUserOrPwdErr",
|
||||||
103: "AccountFrozen",
|
103: "AccountFrozen",
|
||||||
104: "AccountBanned",
|
104: "AccountBanned",
|
||||||
120: "RegisterUserExist",
|
110: "RegisterUserExist",
|
||||||
|
115: "VersionTooLow",
|
||||||
}
|
}
|
||||||
ErrCode_value = map[string]int32{
|
ErrCode_value = map[string]int32{
|
||||||
"OK": 0,
|
"OK": 0,
|
||||||
@ -51,7 +53,8 @@ var (
|
|||||||
"LoginUserOrPwdErr": 102,
|
"LoginUserOrPwdErr": 102,
|
||||||
"AccountFrozen": 103,
|
"AccountFrozen": 103,
|
||||||
"AccountBanned": 104,
|
"AccountBanned": 104,
|
||||||
"RegisterUserExist": 120,
|
"RegisterUserExist": 110,
|
||||||
|
"VersionTooLow": 115,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ var File_code_proto protoreflect.FileDescriptor
|
|||||||
const file_code_proto_rawDesc = "" +
|
const file_code_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"code.proto\x12\x02pb*\x86\x01\n" +
|
"code.proto\x12\x02pb*\x99\x01\n" +
|
||||||
"\aErrCode\x12\x06\n" +
|
"\aErrCode\x12\x06\n" +
|
||||||
"\x02OK\x10\x00\x12\r\n" +
|
"\x02OK\x10\x00\x12\r\n" +
|
||||||
"\tSystemErr\x10\x01\x12\x10\n" +
|
"\tSystemErr\x10\x01\x12\x10\n" +
|
||||||
@ -95,7 +98,8 @@ const file_code_proto_rawDesc = "" +
|
|||||||
"\x11LoginUserOrPwdErr\x10f\x12\x11\n" +
|
"\x11LoginUserOrPwdErr\x10f\x12\x11\n" +
|
||||||
"\rAccountFrozen\x10g\x12\x11\n" +
|
"\rAccountFrozen\x10g\x12\x11\n" +
|
||||||
"\rAccountBanned\x10h\x12\x15\n" +
|
"\rAccountBanned\x10h\x12\x15\n" +
|
||||||
"\x11RegisterUserExist\x10xB\x11Z\x0fcommon/proto/pbb\x06proto3"
|
"\x11RegisterUserExist\x10n\x12\x11\n" +
|
||||||
|
"\rVersionTooLow\x10sB\x11Z\x0fcommon/proto/pbb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_code_proto_rawDescOnce sync.Once
|
file_code_proto_rawDescOnce sync.Once
|
||||||
|
@ -22,7 +22,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 命名规则:
|
// 命名规则:
|
||||||
// 1. 所有游戏id都在msgId.proto的MsgId中定义,前缀需有C2S,S2C,Ntf三种之一,后缀统一为Id
|
// 1. 所有消息id都在msgId.proto的MsgId中定义,前缀需有C2S,S2C,Ntf三种之一,后缀统一为Id
|
||||||
// 2. 所有错误码都在code.proto的ErrCode中定义
|
// 2. 所有错误码都在code.proto的ErrCode中定义
|
||||||
// 3. 所有消息名为对应消息id去掉后缀Id组成
|
// 3. 所有消息名为对应消息id去掉后缀Id组成
|
||||||
type MsgId int32
|
type MsgId int32
|
||||||
|
@ -28,6 +28,7 @@ const (
|
|||||||
ServiceTypeId_STI_Gate ServiceTypeId = 100 // 网关id
|
ServiceTypeId_STI_Gate ServiceTypeId = 100 // 网关id
|
||||||
ServiceTypeId_STI_Login ServiceTypeId = 101 // 登陆服
|
ServiceTypeId_STI_Login ServiceTypeId = 101 // 登陆服
|
||||||
ServiceTypeId_STI_Chat ServiceTypeId = 102 // 聊天服
|
ServiceTypeId_STI_Chat ServiceTypeId = 102 // 聊天服
|
||||||
|
ServiceTypeId_STI_DB ServiceTypeId = 103 // db服
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for ServiceTypeId.
|
// Enum value maps for ServiceTypeId.
|
||||||
@ -37,12 +38,14 @@ var (
|
|||||||
100: "STI_Gate",
|
100: "STI_Gate",
|
||||||
101: "STI_Login",
|
101: "STI_Login",
|
||||||
102: "STI_Chat",
|
102: "STI_Chat",
|
||||||
|
103: "STI_DB",
|
||||||
}
|
}
|
||||||
ServiceTypeId_value = map[string]int32{
|
ServiceTypeId_value = map[string]int32{
|
||||||
"STI_Unknown": 0,
|
"STI_Unknown": 0,
|
||||||
"STI_Gate": 100,
|
"STI_Gate": 100,
|
||||||
"STI_Login": 101,
|
"STI_Login": 101,
|
||||||
"STI_Chat": 102,
|
"STI_Chat": 102,
|
||||||
|
"STI_DB": 103,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,12 +80,14 @@ var File_service_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_service_proto_rawDesc = "" +
|
const file_service_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\rservice.proto\x12\x02pb*K\n" +
|
"\rservice.proto\x12\x02pb*W\n" +
|
||||||
"\rServiceTypeId\x12\x0f\n" +
|
"\rServiceTypeId\x12\x0f\n" +
|
||||||
"\vSTI_Unknown\x10\x00\x12\f\n" +
|
"\vSTI_Unknown\x10\x00\x12\f\n" +
|
||||||
"\bSTI_Gate\x10d\x12\r\n" +
|
"\bSTI_Gate\x10d\x12\r\n" +
|
||||||
"\tSTI_Login\x10e\x12\f\n" +
|
"\tSTI_Login\x10e\x12\f\n" +
|
||||||
"\bSTI_Chat\x10fB\x11Z\x0fcommon/proto/pbb\x06proto3"
|
"\bSTI_Chat\x10f\x12\n" +
|
||||||
|
"\n" +
|
||||||
|
"\x06STI_DB\x10gB\x11Z\x0fcommon/proto/pbb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_service_proto_rawDescOnce sync.Once
|
file_service_proto_rawDescOnce sync.Once
|
||||||
|
8
common/rpcName/rpcName.go
Normal file
8
common/rpcName/rpcName.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package rpcName
|
||||||
|
|
||||||
|
const (
|
||||||
|
GetUserAccount = "get.user.account.rpc"
|
||||||
|
CreateUserAccount = "create.user.account.rpc"
|
||||||
|
UpdateUserPassword = "update.user.password.rpc"
|
||||||
|
LogUserAccountLogin = "user.login.rpc"
|
||||||
|
)
|
@ -1,7 +0,0 @@
|
|||||||
package rpcName
|
|
||||||
|
|
||||||
const (
|
|
||||||
GetUserAccount = "get.user.account.rpc"
|
|
||||||
CreateUserAccount = "create.user.account.rpc"
|
|
||||||
UpdateUserPassword = "update.user.password.rpc"
|
|
||||||
)
|
|
@ -4,4 +4,5 @@ const (
|
|||||||
Gate = "gate"
|
Gate = "gate"
|
||||||
Chat = "chat"
|
Chat = "chat"
|
||||||
Login = "login"
|
Login = "login"
|
||||||
|
Db = "db"
|
||||||
)
|
)
|
||||||
|
23
common/utils/redis.go
Normal file
23
common/utils/redis.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fox/fox/ksync"
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 动态调整redis池
|
||||||
|
func AutoSetRedisPool(rdb *redis.Client) {
|
||||||
|
// 连接池自动扩展
|
||||||
|
ksync.GoSafe(func() {
|
||||||
|
for {
|
||||||
|
stats := rdb.PoolStats()
|
||||||
|
usage := float64(stats.TotalConns-stats.IdleConns) / float64(stats.TotalConns)
|
||||||
|
if usage > 0.8 { // 使用率超过80%
|
||||||
|
// 动态调整PoolSize
|
||||||
|
rdb.Options().PoolSize = int(float64(rdb.Options().PoolSize) * 1.2)
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
}, nil)
|
||||||
|
}
|
8
common/utils/util.go
Normal file
8
common/utils/util.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func Tie[T any](ret bool, v1, v2 T) T {
|
||||||
|
if ret {
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
return v2
|
||||||
|
}
|
@ -2,6 +2,7 @@ package operation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"game/common/model/user"
|
"game/common/model/user"
|
||||||
|
"game/common/utils"
|
||||||
"game/server/db/config"
|
"game/server/db/config"
|
||||||
"github.com/fox/fox/db"
|
"github.com/fox/fox/db"
|
||||||
"github.com/fox/fox/log"
|
"github.com/fox/fox/log"
|
||||||
@ -25,11 +26,13 @@ func InitRedis() {
|
|||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
utils.AutoSetRedisPool(UserRedis)
|
||||||
AccountRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, 1)
|
AccountRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
utils.AutoSetRedisPool(AccountRedis)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitDb() {
|
func InitDb() {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"game/common/model"
|
"game/common/model"
|
||||||
"game/common/model/user"
|
"game/common/model/user"
|
||||||
"game/common/proto/pb"
|
"game/common/proto/pb"
|
||||||
|
"game/common/serialization"
|
||||||
"game/common/utils"
|
"game/common/utils"
|
||||||
"github.com/fox/fox/log"
|
"github.com/fox/fox/log"
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type UserAccountOp struct {
|
type UserAccountOp struct {
|
||||||
|
logDb *gorm.DB
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
accountRedis *redis.Client
|
accountRedis *redis.Client
|
||||||
accountOp *model.TableOp[user.UserAccount]
|
accountOp *model.TableOp[user.UserAccount]
|
||||||
@ -22,6 +24,7 @@ type UserAccountOp struct {
|
|||||||
|
|
||||||
func NewUserAccountOp() *UserAccountOp {
|
func NewUserAccountOp() *UserAccountOp {
|
||||||
return &UserAccountOp{
|
return &UserAccountOp{
|
||||||
|
logDb: LogDB,
|
||||||
db: UserDB,
|
db: UserDB,
|
||||||
accountRedis: AccountRedis,
|
accountRedis: AccountRedis,
|
||||||
accountOp: model.NewTableOp[user.UserAccount](UserDB, AccountRedis),
|
accountOp: model.NewTableOp[user.UserAccount](UserDB, AccountRedis),
|
||||||
@ -36,12 +39,18 @@ func (s *UserAccountOp) GetUserAccount(username string) (*user.UserAccount, pb.E
|
|||||||
sUid, err := s.accountRedis.Get(context.Background(), s.redisKey(username)).Result()
|
sUid, err := s.accountRedis.Get(context.Background(), s.redisKey(username)).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, redis.Nil) {
|
if errors.Is(err, redis.Nil) {
|
||||||
var us user.UserAccount
|
us := &user.UserAccount{}
|
||||||
err = s.db.Where("username = ?", username).First(&us).Error
|
err = s.db.Where("username = ?", username).First(us).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorF("find user:%v err:%v", username, err)
|
log.ErrorF("find user:%v err:%v", username, err)
|
||||||
return nil, pb.ErrCode_SystemErr
|
return nil, pb.ErrCode_SystemErr
|
||||||
}
|
}
|
||||||
|
// 从db中查到后写入redis,并建立索引
|
||||||
|
if us.Username != "" && us.ID > 0 {
|
||||||
|
_, _ = s.accountOp.Update(us.ID, serialization.StructToMap(us))
|
||||||
|
_ = s.accountRedis.Set(context.Background(), s.redisKey(username), us.ID, model.TableExpire).Err()
|
||||||
|
}
|
||||||
|
return us, pb.ErrCode_OK
|
||||||
} else {
|
} else {
|
||||||
log.ErrorF("find user:%v err:%v", username, err)
|
log.ErrorF("find user:%v err:%v", username, err)
|
||||||
return nil, pb.ErrCode_SystemErr
|
return nil, pb.ErrCode_SystemErr
|
||||||
@ -69,7 +78,8 @@ func (s *UserAccountOp) CreateUserAccount(us *user.UserAccount) (*user.UserAccou
|
|||||||
if code != pb.ErrCode_OK {
|
if code != pb.ErrCode_OK {
|
||||||
return nil, code
|
return nil, code
|
||||||
}
|
}
|
||||||
s.accountRedis.Set(context.Background(), s.redisKey(us.Username), us.ID, model.TableExpire)
|
// 建立索引
|
||||||
|
_ = s.accountRedis.Set(context.Background(), s.redisKey(us.Username), us.ID, model.TableExpire).Err()
|
||||||
return us, pb.ErrCode_OK
|
return us, pb.ErrCode_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,25 +94,18 @@ func (s *UserAccountOp) UpdateUserPassword(us *user.UserAccount) (*user.UserAcco
|
|||||||
var code pb.ErrCode
|
var code pb.ErrCode
|
||||||
us, code = s.accountOp.Update(us.ID, map[string]any{"password": hashedPassword})
|
us, code = s.accountOp.Update(us.ID, map[string]any{"password": hashedPassword})
|
||||||
if code != pb.ErrCode_OK {
|
if code != pb.ErrCode_OK {
|
||||||
s.accountRedis.Expire(context.Background(), s.redisKey(us.Username), model.TableExpire)
|
_ = s.accountRedis.Expire(context.Background(), s.redisKey(us.Username), model.TableExpire).Err()
|
||||||
}
|
}
|
||||||
return us, code
|
return us, code
|
||||||
}
|
}
|
||||||
|
|
||||||
//// 记录登录日志
|
// 记录登录日志
|
||||||
//func (s *UserAccountOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) {
|
func (s *UserAccountOp) RecordLoginLog(logEntry *user.UserLoginLog) {
|
||||||
// logEntry := user.UserLoginLog{
|
if err := s.logDb.Create(&logEntry).Error; err != nil {
|
||||||
// PlayerID: userID,
|
log.ErrorF("记录登录日志失败: %v", err)
|
||||||
// LoginIP: ip,
|
}
|
||||||
// DeviceInfo: deviceID,
|
}
|
||||||
// LoginResult: success,
|
|
||||||
// FailReason: failReason,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if err := s.logDb.Create(&logEntry).Error; err != nil {
|
|
||||||
// log.ErrorF("记录登录日志失败: %v", err)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
//
|
||||||
//// 生成JWT令牌(简化版)
|
//// 生成JWT令牌(简化版)
|
||||||
//func generateToken(userID uint, username string) (string, error) {
|
//func generateToken(userID uint, username string) (string, error) {
|
||||||
|
83
server/db/server/handlerUser.go
Normal file
83
server/db/server/handlerUser.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"game/common/model/user"
|
||||||
|
"game/common/proto/pb"
|
||||||
|
"game/server/db/operation"
|
||||||
|
"github.com/fox/fox/ipb"
|
||||||
|
"github.com/fox/fox/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 获取帐号
|
||||||
|
func (s *DbService) onGetUserAccount(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
||||||
|
operationDb[user.UserAccount](iMsg, func(us *user.UserAccount) (*user.UserAccount, pb.ErrCode) {
|
||||||
|
return operation.NewUserAccountOp().GetUserAccount(us.Username)
|
||||||
|
})
|
||||||
|
return iMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建帐号
|
||||||
|
func (s *DbService) onCreateUserAccount(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
||||||
|
operationDb[user.UserAccount](iMsg, operation.NewUserAccountOp().CreateUserAccount)
|
||||||
|
return iMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
type result[T any] struct {
|
||||||
|
ret T
|
||||||
|
}
|
||||||
|
|
||||||
|
// 装饰器
|
||||||
|
func operationDb[T any](iMsg *ipb.InternalMsg, operation func(table *T) (*T, pb.ErrCode)) {
|
||||||
|
t := result[T]{}
|
||||||
|
err := json.Unmarshal(iMsg.Msg, &t.ret)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorF("error unmarshalling user account %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
table, code := operation(&t.ret)
|
||||||
|
if code != pb.ErrCode_OK {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
iMsg.Msg, err = json.Marshal(table)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorF("error marshalling user account %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改密码
|
||||||
|
func (s *DbService) onUpdateUserAccount(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
||||||
|
operationDb[user.UserAccount](iMsg, operation.NewUserAccountOp().UpdateUserPassword)
|
||||||
|
return iMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志操作无需返回值
|
||||||
|
func (s *DbService) onLogUserAccountLogin(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
||||||
|
operationDb[user.UserLoginLog](iMsg, func(us *user.UserLoginLog) (*user.UserLoginLog, pb.ErrCode) {
|
||||||
|
operation.NewUserAccountOp().RecordLoginLog(us)
|
||||||
|
return nil, pb.ErrCode_OK
|
||||||
|
})
|
||||||
|
return iMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (s *DbService) operation(iMsg *ipb.InternalMsg, operation func(us *user.UserAccount) (*user.UserAccount, pb.ErrCode)) {
|
||||||
|
// us := &user.UserAccount{}
|
||||||
|
// err := json.Unmarshal(iMsg.Msg, us)
|
||||||
|
// if err != nil {
|
||||||
|
// log.ErrorF("error unmarshalling user account %v", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// var code pb.ErrCode
|
||||||
|
// us, code = operation(us)
|
||||||
|
// if code != pb.ErrCode_OK {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// iMsg.Msg, err = json.Marshal(us)
|
||||||
|
// if err != nil {
|
||||||
|
// log.ErrorF("error marshalling user account %v", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
//}
|
@ -7,8 +7,9 @@ import (
|
|||||||
|
|
||||||
func (s *DbService) initRpcProcessor() {
|
func (s *DbService) initRpcProcessor() {
|
||||||
s.RpcProcessor.RegisterMessages(map[string]processor.RpcHandler{
|
s.RpcProcessor.RegisterMessages(map[string]processor.RpcHandler{
|
||||||
rpcName.CreateUserAccount: s.onCreateUserAccount,
|
rpcName.CreateUserAccount: s.onCreateUserAccount,
|
||||||
rpcName.GetUserAccount: s.onGetUserAccount,
|
rpcName.GetUserAccount: s.onGetUserAccount,
|
||||||
rpcName.UpdateUserPassword: s.onUpdateUserAccount,
|
rpcName.UpdateUserPassword: s.onUpdateUserAccount,
|
||||||
|
rpcName.LogUserAccountLogin: s.onLogUserAccountLogin,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ func Init() {
|
|||||||
log.DebugF("init service begin id:%v, num:%v", config.Command.ServiceId, config.Command.ServiceNum)
|
log.DebugF("init service begin id:%v, num:%v", config.Command.ServiceId, config.Command.ServiceNum)
|
||||||
for i := 0; i < config.Command.ServiceNum; i++ {
|
for i := 0; i < config.Command.ServiceNum; i++ {
|
||||||
sid := config.Command.ServiceId + i
|
sid := config.Command.ServiceId + i
|
||||||
if srv := newLoginService(sid); srv != nil {
|
if srv := newDbService(sid); srv != nil {
|
||||||
DbSrv = append(DbSrv, srv)
|
DbSrv = append(DbSrv, srv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,20 +35,20 @@ func Stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLoginService(serviceId int) *DbService {
|
func newDbService(serviceId int) *DbService {
|
||||||
var err error
|
var err error
|
||||||
s := new(DbService)
|
s := new(DbService)
|
||||||
|
|
||||||
sName := fmt.Sprintf("%v-%d", serviceName.Login, serviceId)
|
sName := fmt.Sprintf("%v-%d", serviceName.Db, serviceId)
|
||||||
if s.NatsService, err = service.NewNatsService(&service.InitNatsServiceParams{
|
if s.NatsService, err = service.NewNatsService(&service.InitNatsServiceParams{
|
||||||
EtcdAddress: config.Cfg.Etcd.Address,
|
EtcdAddress: config.Cfg.Etcd.Address,
|
||||||
EtcdUsername: "",
|
EtcdUsername: "",
|
||||||
EtcdPassword: "",
|
EtcdPassword: "",
|
||||||
NatsAddress: config.Cfg.Nats.Address,
|
NatsAddress: config.Cfg.Nats.Address,
|
||||||
ServiceType: serviceName.Login,
|
ServiceType: serviceName.Db,
|
||||||
ServiceName: sName,
|
ServiceName: sName,
|
||||||
OnFunc: s,
|
OnFunc: s,
|
||||||
TypeId: int(pb.ServiceTypeId_STI_Login),
|
TypeId: int(pb.ServiceTypeId_STI_DB),
|
||||||
Version: config.Cfg.BuildDate,
|
Version: config.Cfg.BuildDate,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
@ -80,18 +80,5 @@ func (s *DbService) OnStop() {
|
|||||||
// 处理其它服发送过来的消息
|
// 处理其它服发送过来的消息
|
||||||
func (s *DbService) OnMessage(data []byte) error {
|
func (s *DbService) OnMessage(data []byte) error {
|
||||||
_ = data
|
_ = data
|
||||||
//log.Debug(s.Log("received message:%v", iMsg.MsgId))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//// 向内部服务发送消息
|
|
||||||
//func (s *DbService) SendServiceData(topic string, connId uint32, userId int64, msgId int32, data []byte) {
|
|
||||||
// iMsg := ipb.MakeMsg(s.Name(), connId, userId, msgId, data)
|
|
||||||
// _ = s.Send(topic, iMsg)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// 向内部服务发送消息
|
|
||||||
//func (s *DbService) SendServiceMsg(topic string, connId uint32, userId int64, msgId int32, msg proto.Message) {
|
|
||||||
// data, _ := proto.Marshal(msg)
|
|
||||||
// s.SendServiceData(topic, connId, userId, msgId, data)
|
|
||||||
//}
|
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"game/common/model/user"
|
|
||||||
"game/common/proto/pb"
|
|
||||||
"game/server/db/operation"
|
|
||||||
"github.com/fox/fox/ipb"
|
|
||||||
"github.com/fox/fox/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 获取帐号
|
|
||||||
func (s *DbService) onGetUserAccount(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
|
||||||
s.operation(iMsg, func(us *user.UserAccount) (*user.UserAccount, pb.ErrCode) {
|
|
||||||
return operation.NewUserAccountOp().GetUserAccount(us.Username)
|
|
||||||
})
|
|
||||||
return iMsg
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建帐号
|
|
||||||
func (s *DbService) onCreateUserAccount(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
|
||||||
s.operation(iMsg, operation.NewUserAccountOp().CreateUserAccount)
|
|
||||||
return iMsg
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改密码
|
|
||||||
func (s *DbService) operation(iMsg *ipb.InternalMsg, operation func(us *user.UserAccount) (*user.UserAccount, pb.ErrCode)) {
|
|
||||||
us := &user.UserAccount{}
|
|
||||||
err := json.Unmarshal(iMsg.Msg, us)
|
|
||||||
if err != nil {
|
|
||||||
log.ErrorF("error unmarshalling user account %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var code pb.ErrCode
|
|
||||||
us, code = operation(us)
|
|
||||||
if code != pb.ErrCode_OK {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iMsg.Msg, err = json.Marshal(us)
|
|
||||||
if err != nil {
|
|
||||||
log.ErrorF("error marshalling user account %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改密码
|
|
||||||
func (s *DbService) onUpdateUserAccount(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
|
|
||||||
s.operation(iMsg, operation.NewUserAccountOp().UpdateUserPassword)
|
|
||||||
return iMsg
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"game/common/utils"
|
||||||
"game/server/gate/config"
|
"game/server/gate/config"
|
||||||
"github.com/fox/fox/db"
|
"github.com/fox/fox/db"
|
||||||
"github.com/fox/fox/log"
|
"github.com/fox/fox/log"
|
||||||
@ -16,4 +17,5 @@ func InitRedis() {
|
|||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
utils.AutoSetRedisPool(UserRedis)
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) {
|
|||||||
} else {
|
} else {
|
||||||
log.Error(s.Log("topic:%v not exist.user:%v", topic, conn.UserId()))
|
log.Error(s.Log("topic:%v not exist.user:%v", topic, conn.UserId()))
|
||||||
}
|
}
|
||||||
log.Debug(s.Log("received client:%d user:%v message:%v", conn.Id(), conn.UserId(), msg.MsgId))
|
log.Debug(s.Log("received conn:%d user:%v message:%v", conn.Id(), conn.UserId(), msg.MsgId))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 向内部服务发送消息
|
// 向内部服务发送消息
|
||||||
|
@ -3,7 +3,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"game/server/login/config"
|
"game/server/login/config"
|
||||||
"game/server/login/model"
|
|
||||||
"game/server/login/server"
|
"game/server/login/server"
|
||||||
"github.com/fox/fox/log"
|
"github.com/fox/fox/log"
|
||||||
"os"
|
"os"
|
||||||
@ -12,8 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func initRepo() {
|
func initRepo() {
|
||||||
model.InitRedis()
|
//model.InitRedis()
|
||||||
model.InitDb()
|
//model.InitDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(GitCommit, GitBranch, BuildDate string) {
|
func Run(GitCommit, GitBranch, BuildDate string) {
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"game/server/login/config"
|
// "game/server/login/config"
|
||||||
"github.com/fox/fox/db"
|
// "github.com/fox/fox/db"
|
||||||
"github.com/fox/fox/log"
|
// "github.com/fox/fox/log"
|
||||||
"github.com/go-redis/redis/v8"
|
// "github.com/go-redis/redis/v8"
|
||||||
"gorm.io/gorm"
|
// "gorm.io/gorm"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
var (
|
//var (
|
||||||
UserRedis *redis.Client
|
// UserRedis *redis.Client
|
||||||
UserDB *gorm.DB
|
// UserDB *gorm.DB
|
||||||
LogDB *gorm.DB
|
// LogDB *gorm.DB
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
func InitRedis() {
|
//func InitRedis() {
|
||||||
log.Debug("init redis")
|
// log.Debug("init redis")
|
||||||
var err error
|
// var err error
|
||||||
cfg := &config.Cfg.Redis
|
// cfg := &config.Cfg.Redis
|
||||||
UserRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, 0)
|
// UserRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, 0)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err.Error())
|
// log.Fatal(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func InitDb() {
|
//func InitDb() {
|
||||||
log.Debug("init db")
|
// log.Debug("init db")
|
||||||
var err error
|
// var err error
|
||||||
cfg := &config.Cfg.Mysql
|
// cfg := &config.Cfg.Mysql
|
||||||
UserDB, err = db.InitMysql(cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
|
// UserDB, err = db.InitMysql(cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err.Error())
|
// log.Fatal(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
cfg = &config.Cfg.MysqlLog
|
// cfg = &config.Cfg.MysqlLog
|
||||||
LogDB, err = db.InitMysql(cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
|
// LogDB, err = db.InitMysql(cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err.Error())
|
// log.Fatal(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// 自动迁移game库表结构
|
// // 自动迁移game库表结构
|
||||||
err = UserDB.AutoMigrate(
|
// err = UserDB.AutoMigrate(
|
||||||
&UserAccount{},
|
// &UserAccount{},
|
||||||
)
|
// )
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err.Error())
|
// log.Fatal(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// 自动迁移game_log库表结构
|
// // 自动迁移game_log库表结构
|
||||||
err = LogDB.AutoMigrate(
|
// err = LogDB.AutoMigrate(
|
||||||
&UserLoginLog{},
|
// &UserLoginLog{},
|
||||||
)
|
// )
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err.Error())
|
// log.Fatal(err.Error())
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
@ -1,152 +1,152 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"errors"
|
// "errors"
|
||||||
"github.com/fox/fox/log"
|
// "github.com/fox/fox/log"
|
||||||
"golang.org/x/crypto/bcrypt"
|
// "golang.org/x/crypto/bcrypt"
|
||||||
"gorm.io/gorm"
|
// "gorm.io/gorm"
|
||||||
"time"
|
// "time"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
const (
|
//const (
|
||||||
AccountNormal = 1 // 正常
|
// AccountNormal = 1 // 正常
|
||||||
AccountFrozen = 2 // 冻结
|
// AccountFrozen = 2 // 冻结
|
||||||
AccountBanned = 3 // 封禁
|
// AccountBanned = 3 // 封禁
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
// 玩家账户表
|
//// 玩家账户表
|
||||||
type UserAccount struct {
|
//type UserAccount struct {
|
||||||
gorm.Model
|
// gorm.Model
|
||||||
Username string `gorm:"type:varchar(32);uniqueIndex;not null"` // 用户名
|
// Username string `gorm:"type:varchar(32);uniqueIndex;not null"` // 用户名
|
||||||
Password string `gorm:"type:varchar(255);not null"` // 密码哈希
|
// Password string `gorm:"type:varchar(255);not null"` // 密码哈希
|
||||||
Email string `gorm:"type:varchar(100)"` // 邮箱(可选)
|
// Email string `gorm:"type:varchar(100)"` // 邮箱(可选)
|
||||||
Phone string `gorm:"type:varchar(20)"` // 手机号(可选)
|
// Phone string `gorm:"type:varchar(20)"` // 手机号(可选)
|
||||||
DeviceID string `gorm:"type:varchar(64);index"` // 设备ID
|
// DeviceID string `gorm:"type:varchar(64);index"` // 设备ID
|
||||||
LastLoginIP string `gorm:"type:varchar(45)"` // 最后登录IP(支持IPv6)
|
// LastLoginIP string `gorm:"type:varchar(45)"` // 最后登录IP(支持IPv6)
|
||||||
LastLoginTime time.Time // 最后登录时间
|
// LastLoginTime time.Time // 最后登录时间
|
||||||
Status int `gorm:"type:tinyint;default:1"` // 账号状态 1-正常 2-冻结 3-封禁
|
// Status int `gorm:"type:tinyint;default:1"` // 账号状态 1-正常 2-冻结 3-封禁
|
||||||
RegisterIP string `gorm:"type:varchar(45)"` // 注册IP
|
// RegisterIP string `gorm:"type:varchar(45)"` // 注册IP
|
||||||
RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 注册时间
|
// RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 注册时间
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 玩家登录记录表
|
//// 玩家登录记录表
|
||||||
type UserLoginLog struct {
|
//type UserLoginLog struct {
|
||||||
gorm.Model
|
// gorm.Model
|
||||||
PlayerID uint `gorm:"index"` // 关联玩家ID
|
// PlayerID uint `gorm:"index"` // 关联玩家ID
|
||||||
LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP
|
// LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP
|
||||||
LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间
|
// LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间
|
||||||
DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式)
|
// DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式)
|
||||||
LoginResult bool // 登录结果 true-成功 false-失败
|
// LoginResult bool // 登录结果 true-成功 false-失败
|
||||||
FailReason string `gorm:"type:varchar(100)"` // 失败原因
|
// FailReason string `gorm:"type:varchar(100)"` // 失败原因
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
type UserLoginOp struct {
|
//type UserLoginOp struct {
|
||||||
db *gorm.DB
|
// db *gorm.DB
|
||||||
logDb *gorm.DB
|
// logDb *gorm.DB
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func NewUserLoginOp() *UserLoginOp {
|
//func NewUserLoginOp() *UserLoginOp {
|
||||||
return &UserLoginOp{db: UserDB, logDb: LogDB}
|
// return &UserLoginOp{db: UserDB, logDb: LogDB}
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
var (
|
//var (
|
||||||
ErrUserOrPassword = errors.New("user or password was error")
|
// ErrUserOrPassword = errors.New("user or password was error")
|
||||||
ErrAccountFrozen = errors.New("account frozen")
|
// ErrAccountFrozen = errors.New("account frozen")
|
||||||
ErrAccountBanned = errors.New("account banned")
|
// ErrAccountBanned = errors.New("account banned")
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
func (s *UserLoginOp) Login(username, password, ip, deviceID string) (*UserAccount, error) {
|
//func (s *UserLoginOp) Login(username, password, ip, deviceID string) (*UserAccount, error) {
|
||||||
var user UserAccount
|
// var user UserAccount
|
||||||
err := s.db.Where("username = ?", username).First(&user).Error
|
// err := s.db.Where("username = ?", username).First(&user).Error
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
// 验证密码
|
// // 验证密码
|
||||||
if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
// if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||||
s.recordLoginLog(user.ID, ip, deviceID, false, ErrUserOrPassword.Error())
|
// s.recordLoginLog(user.ID, ip, deviceID, false, ErrUserOrPassword.Error())
|
||||||
return nil, ErrUserOrPassword
|
// return nil, ErrUserOrPassword
|
||||||
}
|
// }
|
||||||
// 检查账号状态
|
// // 检查账号状态
|
||||||
switch user.Status {
|
// switch user.Status {
|
||||||
case AccountNormal:
|
// case AccountNormal:
|
||||||
|
//
|
||||||
case AccountFrozen:
|
// case AccountFrozen:
|
||||||
s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountFrozen.Error())
|
// s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountFrozen.Error())
|
||||||
return nil, ErrAccountFrozen
|
// return nil, ErrAccountFrozen
|
||||||
case AccountBanned:
|
// case AccountBanned:
|
||||||
s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountBanned.Error())
|
// s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountBanned.Error())
|
||||||
return nil, ErrAccountBanned
|
// return nil, ErrAccountBanned
|
||||||
}
|
// }
|
||||||
// 更新最后登录信息
|
// // 更新最后登录信息
|
||||||
user.LastLoginIP = ip
|
// user.LastLoginIP = ip
|
||||||
user.LastLoginTime = time.Now()
|
// user.LastLoginTime = time.Now()
|
||||||
_ = s.db.Save(&user).Error
|
// _ = s.db.Save(&user).Error
|
||||||
|
//
|
||||||
// 记录成功登录日志
|
// // 记录成功登录日志
|
||||||
s.recordLoginLog(user.ID, ip, deviceID, true, "")
|
// s.recordLoginLog(user.ID, ip, deviceID, true, "")
|
||||||
|
//
|
||||||
// 6. 生成访问令牌
|
// // 6. 生成访问令牌
|
||||||
token, err := generateToken(user.ID, user.Username)
|
// token, err := generateToken(user.ID, user.Username)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
user.Password = token
|
// user.Password = token
|
||||||
return &user, err
|
// return &user, err
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 注册新用户
|
//// 注册新用户
|
||||||
func (s *UserLoginOp) RegisterNewUser(username, password, ip, deviceID string) (*UserAccount, error) {
|
//func (s *UserLoginOp) RegisterNewUser(username, password, ip, deviceID string) (*UserAccount, error) {
|
||||||
// 密码加密
|
// // 密码加密
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
user := UserAccount{
|
// user := UserAccount{
|
||||||
Username: username,
|
// Username: username,
|
||||||
Password: string(hashedPassword),
|
// Password: string(hashedPassword),
|
||||||
DeviceID: deviceID,
|
// DeviceID: deviceID,
|
||||||
RegisterIP: ip,
|
// RegisterIP: ip,
|
||||||
Status: 1,
|
// Status: 1,
|
||||||
LastLoginIP: ip,
|
// LastLoginIP: ip,
|
||||||
LastLoginTime: time.Now(),
|
// LastLoginTime: time.Now(),
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if err := s.db.Create(&user).Error; err != nil {
|
// if err := s.db.Create(&user).Error; err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
s.recordLoginLog(user.ID, ip, deviceID, true, "")
|
// s.recordLoginLog(user.ID, ip, deviceID, true, "")
|
||||||
|
//
|
||||||
// 生成访问令牌
|
// // 生成访问令牌
|
||||||
token, err := generateToken(user.ID, user.Username)
|
// token, err := generateToken(user.ID, user.Username)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
user.Password = token
|
// user.Password = token
|
||||||
|
//
|
||||||
return &user, nil
|
// return &user, nil
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 记录登录日志
|
//// 记录登录日志
|
||||||
func (s *UserLoginOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) {
|
//func (s *UserLoginOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) {
|
||||||
logEntry := UserLoginLog{
|
// logEntry := UserLoginLog{
|
||||||
PlayerID: userID,
|
// PlayerID: userID,
|
||||||
LoginIP: ip,
|
// LoginIP: ip,
|
||||||
DeviceInfo: deviceID,
|
// DeviceInfo: deviceID,
|
||||||
LoginResult: success,
|
// LoginResult: success,
|
||||||
FailReason: failReason,
|
// FailReason: failReason,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if err := s.logDb.Create(&logEntry).Error; err != nil {
|
// if err := s.logDb.Create(&logEntry).Error; err != nil {
|
||||||
log.ErrorF("记录登录日志失败: %v", err)
|
// log.ErrorF("记录登录日志失败: %v", err)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 生成JWT令牌(简化版)
|
//// 生成JWT令牌(简化版)
|
||||||
func generateToken(userID uint, username string) (string, error) {
|
//func generateToken(userID uint, username string) (string, error) {
|
||||||
_ = userID
|
// _ = userID
|
||||||
_ = username
|
// _ = username
|
||||||
// 这里应该使用JWT库生成实际令牌
|
// // 这里应该使用JWT库生成实际令牌
|
||||||
// 简化实现,实际项目中请使用安全的JWT实现
|
// // 简化实现,实际项目中请使用安全的JWT实现
|
||||||
return "generated-token-placeholder", nil
|
// return "generated-token-placeholder", nil
|
||||||
}
|
//}
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"encoding/json"
|
||||||
|
"game/common/model/user"
|
||||||
"game/common/proto/pb"
|
"game/common/proto/pb"
|
||||||
"game/server/login/model"
|
"game/common/rpcName"
|
||||||
|
"game/common/utils"
|
||||||
|
"github.com/fox/fox/etcd"
|
||||||
"github.com/fox/fox/ipb"
|
"github.com/fox/fox/ipb"
|
||||||
|
"github.com/fox/fox/ksync"
|
||||||
|
"github.com/fox/fox/log"
|
||||||
"github.com/fox/fox/processor"
|
"github.com/fox/fox/processor"
|
||||||
"github.com/fox/fox/service"
|
"github.com/fox/fox/service"
|
||||||
"gorm.io/gorm"
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeout = time.Second * 30
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *LoginService) initProcessor() {
|
func (s *LoginService) initProcessor() {
|
||||||
@ -16,42 +25,92 @@ func (s *LoginService) initProcessor() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginService) checkLoginOrRegister(req *pb.C2SUserLogin) (user *model.UserAccount, code pb.ErrCode) {
|
func (s *LoginService) checkLoginOrRegister(req *pb.C2SUserLogin) (us *user.UserAccount, code pb.ErrCode, node *etcd.ServiceNode) {
|
||||||
op := model.NewUserLoginOp()
|
|
||||||
var err error
|
var err error
|
||||||
user, err = op.Login(req.Username, req.Password, req.Ip, req.DeviceId)
|
node, err = s.bindService.RandServiceNode(pb.ServiceTypeId_STI_DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
log.ErrorF(s.Log("not find db service.err:%s ", err.Error()))
|
||||||
user, err = op.RegisterNewUser(req.Username, req.Password, req.Ip, req.DeviceId)
|
return nil, pb.ErrCode_SystemErr, node
|
||||||
if err != nil {
|
}
|
||||||
code = pb.ErrCode_RegisterUserExist
|
if req.Version < "20250601123030" {
|
||||||
return
|
return nil, pb.ErrCode_VersionTooLow, node
|
||||||
}
|
}
|
||||||
} else if errors.Is(err, model.ErrUserOrPassword) {
|
us = &user.UserAccount{
|
||||||
code = pb.ErrCode_LoginUserOrPwdErr
|
Username: req.Username,
|
||||||
return
|
Password: req.Password,
|
||||||
} else if errors.Is(err, model.ErrAccountFrozen) {
|
DeviceID: req.DeviceId,
|
||||||
code = pb.ErrCode_AccountFrozen
|
LastLoginIP: req.Ip,
|
||||||
return
|
}
|
||||||
} else if errors.Is(err, model.ErrAccountBanned) {
|
rpcMsg := ipb.MakeRpcMsg[user.UserAccount](rpcName.GetUserAccount, 0, us)
|
||||||
code = pb.ErrCode_AccountBanned
|
rspMsg, err := s.Call(service.RpcTopicEx(node.Name), timeout, rpcMsg)
|
||||||
return
|
if err != nil {
|
||||||
} else {
|
log.ErrorF(s.Log("call rpc:%v err:%s ", rpcMsg.RpcMsgId, err.Error()))
|
||||||
code = pb.ErrCode_SystemErr
|
return nil, pb.ErrCode_SystemErr, node
|
||||||
|
}
|
||||||
|
_ = json.Unmarshal(rspMsg.Msg, us)
|
||||||
|
if us.ID == 0 {
|
||||||
|
// 没有帐号,创建帐号
|
||||||
|
rpcMsg.RpcMsgId = rpcName.CreateUserAccount
|
||||||
|
rspMsg, err = s.Call(service.RpcTopicEx(node.Name), timeout, rpcMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorF(s.Log("call rpc:%v err:%s ", rpcMsg.RpcMsgId, err.Error()))
|
||||||
|
return nil, pb.ErrCode_SystemErr, node
|
||||||
|
}
|
||||||
|
_ = json.Unmarshal(rspMsg.Msg, us)
|
||||||
|
if us.ID == 0 {
|
||||||
|
log.ErrorF(s.Log("call rpc:%v err", rpcMsg.RpcMsgId))
|
||||||
|
return nil, pb.ErrCode_SystemErr, node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user, code
|
if pwd, _ := utils.Password(req.Password); pwd != us.Password {
|
||||||
|
return nil, pb.ErrCode_LoginUserOrPwdErr, node
|
||||||
|
}
|
||||||
|
switch us.Status {
|
||||||
|
case user.AccountFrozen:
|
||||||
|
return nil, pb.ErrCode_AccountFrozen, node
|
||||||
|
case user.AccountBanned:
|
||||||
|
return nil, pb.ErrCode_AccountBanned, node
|
||||||
|
default:
|
||||||
|
return us, pb.ErrCode_OK, node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成JWT令牌(简化版)
|
||||||
|
func generateToken(userID uint, username string) (string, error) {
|
||||||
|
_ = userID
|
||||||
|
_ = username
|
||||||
|
// 这里应该使用JWT库生成实际令牌
|
||||||
|
// 简化实现,实际项目中请使用安全的JWT实现
|
||||||
|
return "generated-token-placeholder", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登录或注册
|
// 登录或注册
|
||||||
func (s *LoginService) onLoginOrRegister(iMsg *ipb.InternalMsg, req *pb.C2SUserLogin) {
|
func (s *LoginService) onLoginOrRegister(iMsg *ipb.InternalMsg, req *pb.C2SUserLogin) {
|
||||||
user, code := s.checkLoginOrRegister(req)
|
ksync.GoSafe(func() {
|
||||||
userId := int64(0)
|
us, code, node := s.checkLoginOrRegister(req)
|
||||||
rsp := &pb.S2CUserLogin{Code: code}
|
userId := int64(0)
|
||||||
if user != nil && code == pb.ErrCode_OK {
|
rsp := &pb.S2CUserLogin{Code: code}
|
||||||
rsp.UserId = int64(user.ID)
|
if us != nil && code == pb.ErrCode_OK {
|
||||||
rsp.Token = user.Password
|
rsp.UserId = int64(us.ID)
|
||||||
userId = rsp.UserId
|
rsp.Token, _ = generateToken(us.ID, us.Username)
|
||||||
}
|
userId = rsp.UserId
|
||||||
s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, userId, int32(pb.MsgId_S2CUserLoginId), rsp)
|
}
|
||||||
|
s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, userId, int32(pb.MsgId_S2CUserLoginId), rsp)
|
||||||
|
|
||||||
|
if us != nil && us.ID > 0 {
|
||||||
|
switch code {
|
||||||
|
case pb.ErrCode_LoginUserOrPwdErr:
|
||||||
|
rpcMsg := ipb.MakeRpcMsg[user.UserLoginLog](rpcName.GetUserAccount, 0, &user.UserLoginLog{
|
||||||
|
UID: us.ID,
|
||||||
|
LoginIP: us.LastLoginIP,
|
||||||
|
LoginTime: time.Now(),
|
||||||
|
DeviceInfo: us.DeviceID,
|
||||||
|
LoginResult: code == pb.ErrCode_OK,
|
||||||
|
FailReason: code.String(),
|
||||||
|
})
|
||||||
|
_, _ = s.Call(service.RpcTopicEx(node.Name), timeout, rpcMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, nil)
|
||||||
}
|
}
|
||||||
|
4
工作.txt
4
工作.txt
@ -1,6 +1,8 @@
|
|||||||
1.测试gate
|
1.测试gate
|
||||||
1.1 每10分钟新起1000个连接,发送登陆,然后关闭。检查是否有内存及协程泄漏。(有泄漏)
|
1.1 每10分钟新起1000个连接,发送登陆,然后关闭。检查是否有内存及协程泄漏。(已修复)
|
||||||
1.2 启动1000个链接,每小时固定发送登陆消息,一天后查看连接是否还在。检查心跳机制。(已修复)
|
1.2 启动1000个链接,每小时固定发送登陆消息,一天后查看连接是否还在。检查心跳机制。(已修复)
|
||||||
|
1.3 修改完后清除调试日志。(已完成)
|
||||||
|
1.4 客户端stop关闭连接,触发服务端连接崩溃。(已修复)
|
||||||
|
|
||||||
2.编写db服
|
2.编写db服
|
||||||
2.1 login服向db服请求数据及向log db服写入日志。测试rpc机制。
|
2.1 login服向db服请求数据及向log db服写入日志。测试rpc机制。
|
||||||
|
Loading…
x
Reference in New Issue
Block a user