This commit is contained in:
liuxiaobo 2025-06-02 01:07:35 +08:00
parent 5aa67f5153
commit e0e44380b7
16 changed files with 357 additions and 277 deletions

View File

@ -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
View 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
}

View 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
}

View 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
}

View File

@ -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
View 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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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())

View 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)
}

View 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
//}

View File

@ -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)
}

View File

@ -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
View 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
}

View File

@ -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
View 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 玩法逻辑