db服
This commit is contained in:
parent
5aa67f5153
commit
e0e44380b7
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"game/common/proto/pb"
|
||||
"game/common/serialization"
|
||||
"game/common/utils"
|
||||
"github.com/fox/fox/log"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tableExpire = 7 * 24 * time.Hour // 七天后过期
|
||||
TableExpire = 7 * 24 * time.Hour // 七天后过期
|
||||
)
|
||||
|
||||
type resultT[T any] struct {
|
||||
@ -33,7 +34,7 @@ type TableOp[T iTable] struct {
|
||||
rds *redis.Client
|
||||
}
|
||||
|
||||
func newTableOp[T iTable](db *gorm.DB, rds *redis.Client) *TableOp[T] {
|
||||
func NewTableOp[T iTable](db *gorm.DB, rds *redis.Client) *TableOp[T] {
|
||||
return &TableOp[T]{db: db, rds: rds}
|
||||
}
|
||||
|
||||
@ -82,7 +83,7 @@ func (s *TableOp[T]) updateRedis(id uint, maps map[string]any) {
|
||||
if err := s.rds.HMSet(context.Background(), s.redisKey(id), maps).Err(); err != nil {
|
||||
log.ErrorF("redis-key:%v HMSet err: %v", s.redisKey(id), err)
|
||||
}
|
||||
_ = s.rds.Expire(context.Background(), s.redisKey(id), tableExpire).Err()
|
||||
_ = s.rds.Expire(context.Background(), s.redisKey(id), TableExpire).Err()
|
||||
}
|
||||
|
||||
func (s *TableOp[T]) deleteRedis(id uint) {
|
||||
@ -97,6 +98,7 @@ func (s *TableOp[T]) Create(t *T) (*T, pb.ErrCode) {
|
||||
log.ErrorF("create table:%v err:%v", s.tableName(), err)
|
||||
return nil, pb.ErrCode_SystemErr
|
||||
}
|
||||
s.writeRedis((*t).GetId(), t)
|
||||
return t, pb.ErrCode_OK
|
||||
}
|
||||
|
||||
@ -114,16 +116,21 @@ func (s *TableOp[T]) Find(id uint) (*T, pb.ErrCode) {
|
||||
return &result.ret, pb.ErrCode_OK
|
||||
}
|
||||
|
||||
//// 根据条件查询,只在mysql中查询,无法在redis中查询
|
||||
//func (s *TableOp[T]) FindCondition(condition map[string]any) (*T, error) {
|
||||
// var result resultT[T]
|
||||
// err := s.db.Where(condition).First(&result.ret).Error
|
||||
// if err != nil {
|
||||
// log.ErrorF("find table:%v condition:%v err:%v", s.tableName(), utils.JsonMarshal(condition), err)
|
||||
// return nil, err
|
||||
// }
|
||||
// return &result.ret, nil
|
||||
//}
|
||||
// 根据条件查询,只在mysql中查询,无法在redis中查询
|
||||
func (s *TableOp[T]) FindCondition(condition map[string]any) (*T, error) {
|
||||
var result resultT[T]
|
||||
err := s.db.Where(condition).First(&result.ret).Error
|
||||
if err != nil {
|
||||
log.ErrorF("find table:%v condition:%v err:%v", s.tableName(), utils.JsonMarshal(condition), err)
|
||||
return nil, err
|
||||
}
|
||||
// 查看redis中是否存在该键,不存在则写入数据
|
||||
exist, _ := s.rds.Exists(context.Background(), s.redisKey(result.ret.GetId())).Result()
|
||||
if exist != 1 {
|
||||
s.writeRedis(result.ret.GetId(), &result.ret)
|
||||
}
|
||||
return &result.ret, nil
|
||||
}
|
||||
|
||||
func (s *TableOp[T]) Update(id uint, updates map[string]any) (*T, pb.ErrCode) {
|
||||
var result resultT[T]
|
19
common/model/user/user.go
Normal file
19
common/model/user/user.go
Normal file
@ -0,0 +1,19 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type User struct {
|
||||
gorm.Model
|
||||
accountId uint `gorm:"type:bigint;uniqueIndex;not null"` // 帐号id
|
||||
Nickname string `gorm:"type:varchar(32);uniqueIndex;not null"` // 昵称
|
||||
AvatarUrl string `gorm:"type:varchar(255)"` // 头像
|
||||
AvatarFrame string `gorm:"type:varchar(255)"` // 头像框
|
||||
VipExp int32 `gorm:"type:int"` // vip经验值
|
||||
}
|
||||
|
||||
func (u User) GetId() uint {
|
||||
return u.ID
|
||||
}
|
46
common/model/user/userAccount.go
Normal file
46
common/model/user/userAccount.go
Normal file
@ -0,0 +1,46 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
AccountNormal = 1 // 正常
|
||||
AccountFrozen = 2 // 冻结
|
||||
AccountBanned = 3 // 封禁
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type UserAccount struct {
|
||||
gorm.Model
|
||||
Username string `gorm:"type:varchar(32);uniqueIndex;not null"` // 用户名
|
||||
Password string `gorm:"type:varchar(255);not null"` // 密码哈希
|
||||
Email string `gorm:"type:varchar(100)"` // 邮箱(可选)
|
||||
Phone string `gorm:"type:varchar(20)"` // 手机号(可选)
|
||||
DeviceID string `gorm:"type:varchar(64);index"` // 设备ID
|
||||
LastLoginIP string `gorm:"type:varchar(45)"` // 最后登录IP(支持IPv6)
|
||||
LastLoginTime time.Time // 最后登录时间
|
||||
Status int `gorm:"type:tinyint;default:1"` // 账号状态 1-正常 2-冻结 3-封禁
|
||||
RegisterIP string `gorm:"type:varchar(45)"` // 注册IP
|
||||
RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 注册时间
|
||||
}
|
||||
|
||||
func (u UserAccount) GetId() uint {
|
||||
return u.ID
|
||||
}
|
||||
|
||||
// 玩家登录记录表
|
||||
type UserLoginLog struct {
|
||||
gorm.Model
|
||||
PlayerID uint `gorm:"index"` // 关联玩家ID
|
||||
LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP
|
||||
LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间
|
||||
DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式)
|
||||
LoginResult bool // 登录结果 true-成功 false-失败
|
||||
FailReason string `gorm:"type:varchar(100)"` // 失败原因
|
||||
}
|
||||
|
||||
func (u UserLoginLog) GetId() uint {
|
||||
return u.ID
|
||||
}
|
19
common/model/user/userResources.go
Normal file
19
common/model/user/userResources.go
Normal file
@ -0,0 +1,19 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type UserResources struct {
|
||||
gorm.Model
|
||||
accountId uint `gorm:"type:bigint;uniqueIndex;not null"` // 帐号id
|
||||
Nickname string `gorm:"type:varchar(32);uniqueIndex;not null"` // 昵称
|
||||
AvatarUrl string `gorm:"type:varchar(255)"` // 头像
|
||||
AvatarFrame string `gorm:"type:varchar(255)"` // 头像框
|
||||
VipExp int32 `gorm:"type:int"` // vip经验值
|
||||
}
|
||||
|
||||
func (u UserResources) GetId() uint {
|
||||
return u.ID
|
||||
}
|
@ -4,13 +4,14 @@ option go_package = "common/proto/pb";
|
||||
|
||||
/*
|
||||
命名规则:
|
||||
1. 所有游戏id都在msgId.proto的MsgId中定义,前缀需有C2S,S2C,Ntf三种之一,后缀统一为Id
|
||||
1. 所有消息id都在msgId.proto的MsgId中定义,前缀需有C2S,S2C,Ntf三种之一,后缀统一为Id
|
||||
2. 所有错误码都在code.proto的ErrCode中定义
|
||||
3. 所有消息名为对应消息id去掉后缀Id组成
|
||||
*/
|
||||
enum MsgId
|
||||
{
|
||||
MI_Unknown = 0;
|
||||
|
||||
// 聊天服 2000-2100
|
||||
C2SChatId = 2000; // 玩家聊天消息
|
||||
S2CChatId = 2001; // 复用C2SChatMsg
|
||||
|
14
common/utils/password.go
Normal file
14
common/utils/password.go
Normal file
@ -0,0 +1,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func Password(password string) (string, error) {
|
||||
// 密码加密
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(hashedPassword), nil
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Nickname string `gorm:"type:varchar(32);uniqueIndex;not null"` // 用户名
|
||||
AvatarUrl string `gorm:"type:varchar(255)"` // 头像
|
||||
AvatarBorder string `gorm:"type:varchar(255)"` // 头像框
|
||||
Gold int64 `gorm:"type:bigint;default:0"` // 金币
|
||||
VipExp int32 `gorm:"type:int"` // vip经验值
|
||||
}
|
||||
|
||||
func (u User) GetId() uint {
|
||||
return u.ID
|
||||
}
|
||||
|
||||
func NewUserOp() *TableOp[User] {
|
||||
return newTableOp[User](UserDB, UserRedis)
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/fox/fox/log"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
AccountNormal = 1 // 正常
|
||||
AccountFrozen = 2 // 冻结
|
||||
AccountBanned = 3 // 封禁
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type UserAccount struct {
|
||||
gorm.Model
|
||||
Username string `gorm:"type:varchar(32);uniqueIndex;not null"` // 用户名
|
||||
Password string `gorm:"type:varchar(255);not null"` // 密码哈希
|
||||
Email string `gorm:"type:varchar(100)"` // 邮箱(可选)
|
||||
Phone string `gorm:"type:varchar(20)"` // 手机号(可选)
|
||||
DeviceID string `gorm:"type:varchar(64);index"` // 设备ID
|
||||
LastLoginIP string `gorm:"type:varchar(45)"` // 最后登录IP(支持IPv6)
|
||||
LastLoginTime time.Time // 最后登录时间
|
||||
Status int `gorm:"type:tinyint;default:1"` // 账号状态 1-正常 2-冻结 3-封禁
|
||||
RegisterIP string `gorm:"type:varchar(45)"` // 注册IP
|
||||
RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 注册时间
|
||||
}
|
||||
|
||||
// 玩家登录记录表
|
||||
type UserLoginLog struct {
|
||||
gorm.Model
|
||||
PlayerID uint `gorm:"index"` // 关联玩家ID
|
||||
LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP
|
||||
LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间
|
||||
DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式)
|
||||
LoginResult bool // 登录结果 true-成功 false-失败
|
||||
FailReason string `gorm:"type:varchar(100)"` // 失败原因
|
||||
}
|
||||
|
||||
type UserLoginOp struct {
|
||||
db *gorm.DB
|
||||
logDb *gorm.DB
|
||||
}
|
||||
|
||||
func NewUserLoginOp() *UserLoginOp {
|
||||
return &UserLoginOp{db: UserDB, logDb: LogDB}
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUserOrPassword = errors.New("user or password was error")
|
||||
ErrAccountFrozen = errors.New("account frozen")
|
||||
ErrAccountBanned = errors.New("account banned")
|
||||
)
|
||||
|
||||
func (s *UserLoginOp) Login(username, password, ip, deviceID string) (*UserAccount, error) {
|
||||
var user UserAccount
|
||||
err := s.db.Where("username = ?", username).First(&user).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 验证密码
|
||||
if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||
s.recordLoginLog(user.ID, ip, deviceID, false, ErrUserOrPassword.Error())
|
||||
return nil, ErrUserOrPassword
|
||||
}
|
||||
// 检查账号状态
|
||||
switch user.Status {
|
||||
case AccountNormal:
|
||||
|
||||
case AccountFrozen:
|
||||
s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountFrozen.Error())
|
||||
return nil, ErrAccountFrozen
|
||||
case AccountBanned:
|
||||
s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountBanned.Error())
|
||||
return nil, ErrAccountBanned
|
||||
}
|
||||
// 更新最后登录信息
|
||||
user.LastLoginIP = ip
|
||||
user.LastLoginTime = time.Now()
|
||||
_ = s.db.Save(&user).Error
|
||||
|
||||
// 记录成功登录日志
|
||||
s.recordLoginLog(user.ID, ip, deviceID, true, "")
|
||||
|
||||
// 6. 生成访问令牌
|
||||
token, err := generateToken(user.ID, user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Password = token
|
||||
return &user, err
|
||||
}
|
||||
|
||||
// 注册新用户
|
||||
func (s *UserLoginOp) RegisterNewUser(username, password, ip, deviceID string) (*UserAccount, error) {
|
||||
// 密码加密
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := UserAccount{
|
||||
Username: username,
|
||||
Password: string(hashedPassword),
|
||||
DeviceID: deviceID,
|
||||
RegisterIP: ip,
|
||||
Status: 1,
|
||||
LastLoginIP: ip,
|
||||
LastLoginTime: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.db.Create(&user).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.recordLoginLog(user.ID, ip, deviceID, true, "")
|
||||
|
||||
// 生成访问令牌
|
||||
token, err := generateToken(user.ID, user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Password = token
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
func (s *UserLoginOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) {
|
||||
logEntry := UserLoginLog{
|
||||
PlayerID: userID,
|
||||
LoginIP: ip,
|
||||
DeviceInfo: deviceID,
|
||||
LoginResult: success,
|
||||
FailReason: failReason,
|
||||
}
|
||||
|
||||
if err := s.logDb.Create(&logEntry).Error; err != nil {
|
||||
log.ErrorF("记录登录日志失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成JWT令牌(简化版)
|
||||
func generateToken(userID uint, username string) (string, error) {
|
||||
_ = userID
|
||||
_ = username
|
||||
// 这里应该使用JWT库生成实际令牌
|
||||
// 简化实现,实际项目中请使用安全的JWT实现
|
||||
return "generated-token-placeholder", nil
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package model
|
||||
package operation
|
||||
|
||||
import (
|
||||
"game/common/model/user"
|
||||
"game/server/db/config"
|
||||
"github.com/fox/fox/db"
|
||||
"github.com/fox/fox/log"
|
||||
@ -9,9 +10,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
UserRedis *redis.Client
|
||||
UserDB *gorm.DB
|
||||
LogDB *gorm.DB
|
||||
UserRedis *redis.Client
|
||||
AccountRedis *redis.Client
|
||||
UserDB *gorm.DB
|
||||
LogDB *gorm.DB
|
||||
)
|
||||
|
||||
func InitRedis() {
|
||||
@ -23,6 +25,11 @@ func InitRedis() {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
AccountRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, 1)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func InitDb() {
|
||||
@ -42,7 +49,7 @@ func InitDb() {
|
||||
}
|
||||
// 自动迁移game库表结构
|
||||
err = UserDB.AutoMigrate(
|
||||
&UserAccount{},
|
||||
&user.UserAccount{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
@ -50,7 +57,7 @@ func InitDb() {
|
||||
}
|
||||
// 自动迁移game_log库表结构
|
||||
err = LogDB.AutoMigrate(
|
||||
&UserLoginLog{},
|
||||
&user.UserLoginLog{},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
16
server/db/operation/operation.go
Normal file
16
server/db/operation/operation.go
Normal file
@ -0,0 +1,16 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"game/common/model"
|
||||
"game/common/model/user"
|
||||
)
|
||||
|
||||
// 玩家表
|
||||
func NewUserOp() *model.TableOp[user.User] {
|
||||
return model.NewTableOp[user.User](UserDB, UserRedis)
|
||||
}
|
||||
|
||||
// 玩家资源表
|
||||
func NewUserResourcesOp() *model.TableOp[user.UserResources] {
|
||||
return model.NewTableOp[user.UserResources](UserDB, UserRedis)
|
||||
}
|
114
server/db/operation/userAccount.go
Normal file
114
server/db/operation/userAccount.go
Normal file
@ -0,0 +1,114 @@
|
||||
package operation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"game/common/model"
|
||||
"game/common/model/user"
|
||||
"game/common/proto/pb"
|
||||
"game/common/utils"
|
||||
"github.com/fox/fox/log"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type UserAccountOp struct {
|
||||
db *gorm.DB
|
||||
accountRedis *redis.Client
|
||||
accountOp *model.TableOp[user.UserAccount]
|
||||
}
|
||||
|
||||
func NewUserAccountOp() *UserAccountOp {
|
||||
return &UserAccountOp{
|
||||
db: UserDB,
|
||||
accountRedis: AccountRedis,
|
||||
accountOp: model.NewTableOp[user.UserAccount](UserDB, AccountRedis),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserAccountOp) redisKey(username string) string {
|
||||
return fmt.Sprintf("username:%s", username)
|
||||
}
|
||||
|
||||
func (s *UserAccountOp) GetUserAccount(username string) (*user.UserAccount, pb.ErrCode) {
|
||||
sUid, err := s.accountRedis.Get(context.Background(), s.redisKey(username)).Result()
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
var us user.UserAccount
|
||||
err = s.db.Where("username = ?", username).First(&us).Error
|
||||
if err != nil {
|
||||
log.ErrorF("find user:%v err:%v", username, err)
|
||||
return nil, pb.ErrCode_SystemErr
|
||||
}
|
||||
} else {
|
||||
log.ErrorF("find user:%v err:%v", username, err)
|
||||
return nil, pb.ErrCode_SystemErr
|
||||
}
|
||||
}
|
||||
uid, _ := strconv.ParseInt(sUid, 10, 64)
|
||||
if uid < 0 {
|
||||
log.ErrorF("get user account:%v failed, uid is: %d", username, uid)
|
||||
return nil, pb.ErrCode_SystemErr
|
||||
}
|
||||
return s.accountOp.Find(uint(uid))
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
func (s *UserAccountOp) CreateUserAccount(us *user.UserAccount) (*user.UserAccount, pb.ErrCode) {
|
||||
// 密码加密
|
||||
hashedPassword, err := utils.Password(us.Password)
|
||||
if err != nil {
|
||||
log.ErrorF("username :%v generate password err:%v", us.Username, err)
|
||||
return nil, pb.ErrCode_SystemErr
|
||||
}
|
||||
us.Password = hashedPassword
|
||||
var code pb.ErrCode
|
||||
us, code = s.accountOp.Create(us)
|
||||
if code != pb.ErrCode_OK {
|
||||
return nil, code
|
||||
}
|
||||
s.accountRedis.Set(context.Background(), s.redisKey(us.Username), us.ID, model.TableExpire)
|
||||
return us, pb.ErrCode_OK
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
func (s *UserAccountOp) UpdateUserPassword(us *user.UserAccount) (*user.UserAccount, pb.ErrCode) {
|
||||
// 密码加密
|
||||
hashedPassword, err := utils.Password(us.Password)
|
||||
if err != nil {
|
||||
log.ErrorF("username :%v generate password err:%v", us.Username, err)
|
||||
return nil, pb.ErrCode_SystemErr
|
||||
}
|
||||
var code pb.ErrCode
|
||||
us, code = s.accountOp.Update(us.ID, map[string]any{"password": hashedPassword})
|
||||
if code != pb.ErrCode_OK {
|
||||
s.accountRedis.Expire(context.Background(), s.redisKey(us.Username), model.TableExpire)
|
||||
}
|
||||
return us, code
|
||||
}
|
||||
|
||||
//// 记录登录日志
|
||||
//func (s *UserAccountOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) {
|
||||
// logEntry := user.UserLoginLog{
|
||||
// PlayerID: userID,
|
||||
// LoginIP: ip,
|
||||
// DeviceInfo: deviceID,
|
||||
// LoginResult: success,
|
||||
// FailReason: failReason,
|
||||
// }
|
||||
//
|
||||
// if err := s.logDb.Create(&logEntry).Error; err != nil {
|
||||
// log.ErrorF("记录登录日志失败: %v", err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 生成JWT令牌(简化版)
|
||||
//func generateToken(userID uint, username string) (string, error) {
|
||||
// _ = userID
|
||||
// _ = username
|
||||
// // 这里应该使用JWT库生成实际令牌
|
||||
// // 简化实现,实际项目中请使用安全的JWT实现
|
||||
// return "generated-token-placeholder", nil
|
||||
//}
|
@ -1,57 +1,14 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"game/common/proto/pb"
|
||||
"game/server/db/model"
|
||||
"github.com/fox/fox/ipb"
|
||||
"game/common/rpcName"
|
||||
"github.com/fox/fox/processor"
|
||||
"github.com/fox/fox/service"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (s *DbService) initProcessor() {
|
||||
s.processor.RegisterMessages(processor.RegisterMetas{
|
||||
pb.MsgId_C2SUserLoginId: {pb.C2SUserLogin{}, s.onLoginOrRegister},
|
||||
func (s *DbService) initRpcProcessor() {
|
||||
s.RpcProcessor.RegisterMessages(map[string]processor.RpcHandler{
|
||||
rpcName.CreateUserAccount: s.onCreateUserAccount,
|
||||
rpcName.GetUserAccount: s.onGetUserAccount,
|
||||
rpcName.UpdateUserPassword: s.onUpdateUserAccount,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *DbService) checkLoginOrRegister(req *pb.C2SUserLogin) (user *model.UserAccount, code pb.ErrCode) {
|
||||
op := model.NewUserLoginOp()
|
||||
var err error
|
||||
user, err = op.Login(req.Username, req.Password, req.Ip, req.DeviceId)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
user, err = op.RegisterNewUser(req.Username, req.Password, req.Ip, req.DeviceId)
|
||||
if err != nil {
|
||||
code = pb.ErrCode_RegisterUserExist
|
||||
return
|
||||
}
|
||||
} else if errors.Is(err, model.ErrUserOrPassword) {
|
||||
code = pb.ErrCode_LoginUserOrPwdErr
|
||||
return
|
||||
} else if errors.Is(err, model.ErrAccountFrozen) {
|
||||
code = pb.ErrCode_AccountFrozen
|
||||
return
|
||||
} else if errors.Is(err, model.ErrAccountBanned) {
|
||||
code = pb.ErrCode_AccountBanned
|
||||
return
|
||||
} else {
|
||||
code = pb.ErrCode_SystemErr
|
||||
}
|
||||
}
|
||||
return user, code
|
||||
}
|
||||
|
||||
// 登录或注册
|
||||
func (s *DbService) onLoginOrRegister(iMsg *ipb.InternalMsg, req *pb.C2SUserLogin) {
|
||||
user, code := s.checkLoginOrRegister(req)
|
||||
userId := int64(0)
|
||||
rsp := &pb.S2CUserLogin{Code: code}
|
||||
if user != nil && code == pb.ErrCode_OK {
|
||||
rsp.UserId = int64(user.ID)
|
||||
rsp.Token = user.Password
|
||||
userId = rsp.UserId
|
||||
}
|
||||
s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, userId, int32(pb.MsgId_S2CUserLoginId), rsp)
|
||||
}
|
||||
|
@ -4,22 +4,15 @@ import (
|
||||
"fmt"
|
||||
"game/common/proto/pb"
|
||||
"game/common/serviceName"
|
||||
"game/common/userBindService"
|
||||
"game/server/db/config"
|
||||
"game/server/db/model"
|
||||
"github.com/fox/fox/ipb"
|
||||
"github.com/fox/fox/log"
|
||||
"github.com/fox/fox/processor"
|
||||
"github.com/fox/fox/service"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
var DbSrv []*DbService
|
||||
|
||||
type DbService struct {
|
||||
*service.NatsService
|
||||
processor *processor.Processor
|
||||
bindService *userBindService.UserBindService
|
||||
}
|
||||
|
||||
func Init() {
|
||||
@ -62,9 +55,7 @@ func newLoginService(serviceId int) *DbService {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.bindService = userBindService.NewUserBindService(model.UserRedis, s.ServiceEtcd())
|
||||
s.processor = processor.NewProcessor()
|
||||
s.initProcessor()
|
||||
s.initRpcProcessor()
|
||||
s.OnInit()
|
||||
return s
|
||||
}
|
||||
@ -88,29 +79,19 @@ func (s *DbService) OnStop() {
|
||||
|
||||
// 处理其它服发送过来的消息
|
||||
func (s *DbService) OnMessage(data []byte) error {
|
||||
var iMsg = &ipb.InternalMsg{}
|
||||
var err error
|
||||
if err = proto.Unmarshal(data, iMsg); err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
if req, err := s.processor.Unmarshal(iMsg.MsgId, iMsg.Msg); err == nil {
|
||||
err = s.processor.Dispatch(iMsg.MsgId, iMsg, req)
|
||||
} else {
|
||||
log.Error(err.Error())
|
||||
}
|
||||
_ = data
|
||||
//log.Debug(s.Log("received message:%v", iMsg.MsgId))
|
||||
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)
|
||||
}
|
||||
//// 向内部服务发送消息
|
||||
//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)
|
||||
//}
|
||||
|
51
server/db/server/user.go
Normal file
51
server/db/server/user.go
Normal file
@ -0,0 +1,51 @@
|
||||
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
|
||||
}
|
@ -172,6 +172,9 @@ func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) {
|
||||
log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
if msg.MsgId < 0 {
|
||||
return
|
||||
}
|
||||
var topic string
|
||||
if msg.ServiceName != "" {
|
||||
topic = service.TopicEx(msg.ServiceName)
|
||||
@ -189,7 +192,7 @@ func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) {
|
||||
} else {
|
||||
log.Error(s.Log("topic:%v not exist.user:%v", topic, conn.UserId()))
|
||||
}
|
||||
log.Debug(s.Log("received client message:%v", msg.MsgId))
|
||||
log.Debug(s.Log("received client:%d user:%v message:%v", conn.Id(), conn.UserId(), msg.MsgId))
|
||||
}
|
||||
|
||||
// 向内部服务发送消息
|
||||
|
20
工作.txt
Normal file
20
工作.txt
Normal file
@ -0,0 +1,20 @@
|
||||
1.测试gate
|
||||
1.1 每10分钟新起1000个连接,发送登陆,然后关闭。检查是否有内存及协程泄漏。
|
||||
1.2 启动1000个链接,每小时固定发送登陆消息,一天后查看连接是否还在。检查心跳机制。
|
||||
|
||||
2.编写db服
|
||||
2.1 login服向db服请求数据及向log db服写入日志。测试rpc机制。
|
||||
|
||||
3.编写color game玩法
|
||||
3.1 服务端玩法
|
||||
3.2 客户端用控制台编写逻辑,不涉及ui
|
||||
|
||||
4. 编写管理后台
|
||||
4.1 玩法配置
|
||||
4.2 金流查询
|
||||
4.3 牌局日志
|
||||
|
||||
5. 客户端编写 u3d
|
||||
5.1 网络连接
|
||||
5.2 ui,动画等
|
||||
5.3 玩法逻辑
|
Loading…
x
Reference in New Issue
Block a user