From 11cc55751874961464dc429ab8a337d311fe26d1 Mon Sep 17 00:00:00 2001 From: liuxiaobo <1224730913@qq.com> Date: Wed, 4 Jun 2025 02:40:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Ddb=E6=9C=8D=EF=BC=8Clogin?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91bug=EF=BC=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/model/tableOperation.go | 10 +-- common/model/tableOperation_test.go | 21 +++++++ common/model/user/userAccount.go | 22 +++---- common/serialization/serialization.go | 87 ++++++++++++++++++++++++++- go.mod | 12 ++-- 工作.txt | 5 +- 6 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 common/model/tableOperation_test.go diff --git a/common/model/tableOperation.go b/common/model/tableOperation.go index e85449c..6bac8f9 100644 --- a/common/model/tableOperation.go +++ b/common/model/tableOperation.go @@ -2,7 +2,6 @@ package model import ( "context" - "encoding/json" "fmt" "game/common/proto/pb" "game/common/serialization" @@ -60,10 +59,13 @@ func (s *TableOp[T]) findByRedis(id uint) *T { if len(maps) == 0 { return nil } - jsonByte, _ := json.Marshal(maps) var result resultT[T] - _ = json.Unmarshal(jsonByte, &result.ret) - log.DebugF("findByRedis redis-key:%v result:%v", s.redisKey(id), result.ret) + us, err := serialization.MapStringToStruct[T](maps) + if err != nil { + log.ErrorF("serialization map to struct err: %v", err) + return nil + } + log.DebugF("findByRedis redis-key:%v result:%v", s.redisKey(id), us) return &result.ret } diff --git a/common/model/tableOperation_test.go b/common/model/tableOperation_test.go new file mode 100644 index 0000000..14bd6a4 --- /dev/null +++ b/common/model/tableOperation_test.go @@ -0,0 +1,21 @@ +package model + +import ( + "encoding/json" + "game/common/model/user" + "game/common/serialization" + "testing" +) + +func Test_zz(t *testing.T) { + zz := `{\"device_id\":\"\",\"email\":\"\",\"id\":\"17\",\"last_login_ip\":\"114.132.124.145:49486\",\"last_login_time\":\"2025-06-04T02:01:49.72898394+08:00\",\"password\":\"$2a$10$p4GJtWzmhaUcT0i/SNpsWOE6uNq8JzymuB6Lv2Qf0Bpg32YthnWgK\",\"phone\":\"\",\"register_ip\":\"114.132.124.145:49486\",\"register_time\":\"0001-01-01T00:00:00Z\",\"status\":\"1\",\"username\":\"test0001\"}` + us := &user.UserAccount{} + _ = json.Unmarshal([]byte(zz), us) + + var err error + us, err = serialization.MapStringToStruct[user.UserAccount](map[string]string{ + "id": "17", + }) + t.Log(err) + t.Log(us) +} diff --git a/common/model/user/userAccount.go b/common/model/user/userAccount.go index 1db70b8..e94ea08 100644 --- a/common/model/user/userAccount.go +++ b/common/model/user/userAccount.go @@ -12,17 +12,17 @@ const ( // 玩家账户表 type UserAccount struct { - ID uint `gorm:"primarykey" json:"id,omitempty"` - Username string `gorm:"type:varchar(32);uniqueIndex;not null" json:"username,omitempty"` // 用户名 - Password string `gorm:"type:varchar(255);not null" json:"password,omitempty"` // 密码哈希 - Email string `gorm:"type:varchar(100)" json:"email,omitempty"` // 邮箱(可选) - Phone string `gorm:"type:varchar(20)" json:"phone,omitempty"` // 手机号(可选) - DeviceID string `gorm:"type:varchar(64);index" json:"device_id"` // 设备ID - LastLoginIP string `gorm:"type:varchar(45)" json:"last_login_ip"` // 最后登录IP(支持IPv6) - LastLoginTime time.Time `json:"last_login_time,omitempty"` // 最后登录时间 - Status int `gorm:"type:tinyint;default:1" json:"status,omitempty"` // 账号状态 1-正常 2-冻结 3-封禁 - RegisterIP string `gorm:"type:varchar(45)" json:"register_ip,omitempty"` // 注册IP - RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP" json:"register_time"` // 注册时间 + ID uint `gorm:"primarykey" json:"id"` + Username string `gorm:"type:varchar(32);uniqueIndex;not null" json:"username"` // 用户名 + Password string `gorm:"type:varchar(255);not null" json:"password"` // 密码哈希 + Email string `gorm:"type:varchar(100)" json:"email"` // 邮箱(可选) + Phone string `gorm:"type:varchar(20)" json:"phone"` // 手机号(可选) + DeviceID string `gorm:"type:varchar(64);index" json:"device_id"` // 设备ID + LastLoginIP string `gorm:"type:varchar(45)" json:"last_login_ip"` // 最后登录IP(支持IPv6) + LastLoginTime time.Time `json:"last_login_time"` // 最后登录时间 + Status int `gorm:"type:tinyint;default:1" json:"status"` // 账号状态 1-正常 2-冻结 3-封禁 + RegisterIP string `gorm:"type:varchar(45)" json:"register_ip"` // 注册IP + RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP" json:"register_time"` // 注册时间 } func (u UserAccount) GetId() uint { diff --git a/common/serialization/serialization.go b/common/serialization/serialization.go index 0e15579..0c3a1dc 100644 --- a/common/serialization/serialization.go +++ b/common/serialization/serialization.go @@ -1,6 +1,11 @@ package serialization -import "reflect" +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" +) func StructToMap(obj interface{}) map[string]interface{} { out := make(map[string]interface{}) @@ -19,3 +24,83 @@ func StructToMap(obj interface{}) map[string]interface{} { } return out } + +func keyValueToField(key, value string, fieldValue *reflect.Value) error { + switch fieldValue.Kind() { + case reflect.Int8, reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64: + if v, err := strconv.ParseInt(value, 10, 0); err == nil { + fieldValue.SetInt(v) + } else { + return err + } + case reflect.Uint8, reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if v, err := strconv.ParseUint(value, 10, 0); err == nil { + fieldValue.SetUint(v) + } else { + return err + } + case reflect.Float32, reflect.Float64: + if v, err := strconv.ParseFloat(value, 0); err == nil { + fieldValue.SetFloat(v) + } else { + return err + } + case reflect.String: + fieldValue.SetString(value) + case reflect.Bool: + if v, err := strconv.ParseBool(value); err == nil { + fieldValue.SetBool(v) + } else { + return err + } + default: + nv := reflect.New(fieldValue.Type()) + if err := json.Unmarshal([]byte(value), nv.Interface()); err == nil { + if fieldValue.Kind() == nv.Elem().Kind() { + fieldValue.Set(nv.Elem()) + } else { + return fmt.Errorf("field:%v should have same type.original type:%v, reflect type:%v", key, fieldValue.Kind().String(), nv.Elem().Kind().String()) + } + } else { + return err + } + } + return nil +} + +func deserializeMapString(kvs map[string]string, structObj interface{}) error { + val := reflect.ValueOf(structObj).Elem() + typ := val.Type() + + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + // 检查字段是否可设置(即导出) + if !field.IsExported() { + continue + } + fieldName := field.Tag.Get("json") + if fieldName == "-" { + continue + } + if value, ok := kvs[fieldName]; ok { + // 获取字段的反射值 + fieldValue := val.FieldByName(field.Name) + if err := keyValueToField(field.Tag.Get("json"), value, &fieldValue); err != nil { + return err + } + } else { + return fmt.Errorf("redis:%v not has field:%v ", typ.Name(), field.Tag.Get("json")) + } + } + return nil +} + +type resultT[T any] struct { + ret T +} + +func MapStringToStruct[T any](maps map[string]string) (*T, error) { + result := &resultT[T]{} + err := deserializeMapString(maps, &result.ret) + return &result.ret, err +} diff --git a/go.mod b/go.mod index f50db9b..f746c16 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,13 @@ require ( go.uber.org/zap v1.27.0 // indirect ) -require google.golang.org/protobuf v1.33.0 +require ( + github.com/golang/protobuf v1.5.4 + github.com/nats-io/nats.go v1.42.0 + golang.org/x/crypto v0.38.0 + google.golang.org/protobuf v1.33.0 + gorm.io/gorm v1.26.1 +) require ( github.com/ClickHouse/ch-go v0.65.1 // indirect @@ -26,13 +32,11 @@ require ( github.com/go-faster/errors v0.7.1 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/nats-io/nats.go v1.42.0 // indirect github.com/nats-io/nkeys v0.4.11 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/orcaman/concurrent-map/v2 v2.0.1 // indirect @@ -45,7 +49,6 @@ require ( go.etcd.io/etcd/client/v3 v3.5.19 // indirect go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/crypto v0.38.0 // indirect golang.org/x/net v0.40.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect @@ -54,7 +57,6 @@ require ( google.golang.org/grpc v1.62.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.5.7 // indirect - gorm.io/gorm v1.26.1 // indirect ) // 关键:指向本地公共库路径[3,5](@ref) diff --git a/工作.txt b/工作.txt index ba6b702..3573453 100644 --- a/工作.txt +++ b/工作.txt @@ -7,8 +7,9 @@ 2.编写db服 2.1 login服向db服请求数据及向log db服写入日志。测试rpc机制。(已验证) 2.2 db创建表时,主键不会自增,而是随机增加。 - 2.3 首次创建帐号,redis没有写入帐号数据。 - 2.4 第二次登陆,login还会走创建帐号逻辑,导致db服返回重复建号失败。 + 2.3 首次创建帐号,redis没有写入帐号数据。 (已修复) + 2.4 第二次登陆,login还会走创建帐号逻辑,导致db服返回重复建号失败。 (已修复) + 2.5 redis写入数据的字段名有误。(已修复) 2.5 清理登陆相关调试日志。 2.6 login在创建帐号时,还需要创建user。