修复db服,login业务逻辑bug,修复反序列化bug

This commit is contained in:
liuxiaobo 2025-06-04 02:40:13 +08:00
parent 9fdb2df2b6
commit 11cc557518
6 changed files with 134 additions and 23 deletions

View File

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

View File

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

View File

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

View File

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

12
go.mod
View File

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

View File

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