序列化使用第三方库
This commit is contained in:
parent
62abce7bca
commit
165efd717f
@ -1,12 +1,8 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type User struct {
|
||||
gorm.Model `json:"-"`
|
||||
ID uint `gorm:"primarykey;autoIncrement" json:"id"`
|
||||
accountId uint `gorm:"type:bigint;uniqueIndex;not null"` // 帐号id
|
||||
Nickname string `gorm:"type:varchar(32);uniqueIndex;not null"` // 昵称
|
||||
AvatarUrl string `gorm:"type:varchar(255)"` // 头像
|
||||
|
@ -5,14 +5,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
//AccountNormal = 1 // 正常
|
||||
// AccountNormal = 1 // 正常
|
||||
AccountFrozen = 2 // 冻结
|
||||
AccountBanned = 3 // 封禁
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type UserAccount struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
ID uint `gorm:"primarykey;autoIncrement" 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"` // 邮箱(可选)
|
||||
@ -35,7 +35,7 @@ func (u UserAccount) TableName() string {
|
||||
|
||||
// 玩家登录记录表
|
||||
type UserLoginLog struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
ID uint `gorm:"primarykey;autoIncrement" json:"id"`
|
||||
UID uint `gorm:"index" json:"uid"` // 关联玩家ID
|
||||
LoginIP string `gorm:"type:varchar(45);not null" json:"login_ip"` // 登录IP
|
||||
LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP" json:"login_time"` // 登录时间
|
||||
|
@ -1,12 +1,8 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 玩家账户表
|
||||
type UserResources struct {
|
||||
gorm.Model `json:"-"`
|
||||
ID uint `gorm:"primarykey;autoIncrement" json:"id"`
|
||||
accountId uint `gorm:"type:bigint;uniqueIndex;not null"` // 帐号id
|
||||
Nickname string `gorm:"type:varchar(32);uniqueIndex;not null"` // 昵称
|
||||
AvatarUrl string `gorm:"type:varchar(255)"` // 头像
|
||||
|
@ -1,106 +1,149 @@
|
||||
package serialization
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fox/fox/log"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func StructToMap(obj interface{}) map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
func StructToMap(_struct interface{}) map[string]interface{} {
|
||||
var result map[string]interface{}
|
||||
err := mapstructure.Decode(_struct, &result)
|
||||
if err != nil {
|
||||
log.ErrorF("struct:%v to map error:%v", _struct, err)
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
t := v.Type()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
key := field.Tag.Get("json") // 使用 json tag 作为字段名
|
||||
if key == "" {
|
||||
key = field.Name
|
||||
}
|
||||
out[key] = v.Field(i).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
|
||||
return result
|
||||
}
|
||||
|
||||
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
|
||||
func stringToTimeHook(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
|
||||
if t == reflect.TypeOf(time.Time{}) && f == reflect.TypeOf("") {
|
||||
return time.Parse(time.RFC3339, data.(string))
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func MapToStruct[T any](maps map[string]string) (*T, error) {
|
||||
result := &resultT[T]{}
|
||||
config := &mapstructure.DecoderConfig{
|
||||
WeaklyTypedInput: true, // 允许弱类型转换(如 "1" → 1)
|
||||
DecodeHook: stringToTimeHook, // 自定义钩子
|
||||
Result: &result.ret,
|
||||
}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
if err != nil {
|
||||
log.ErrorF("map:%v to struct error:%v", maps, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = decoder.Decode(maps)
|
||||
if err != nil {
|
||||
log.ErrorF("map:%v to struct error:%v", maps, err)
|
||||
return nil, err
|
||||
}
|
||||
return &result.ret, nil
|
||||
}
|
||||
|
||||
// func StructToMap(obj interface{}) map[string]interface{} {
|
||||
// out := make(map[string]interface{})
|
||||
// v := reflect.ValueOf(obj)
|
||||
// if v.Kind() == reflect.Ptr {
|
||||
// v = v.Elem()
|
||||
// }
|
||||
// t := v.Type()
|
||||
// for i := 0; i < v.NumField(); i++ {
|
||||
// field := t.Field(i)
|
||||
// key := field.Tag.Get("json") // 使用 json tag 作为字段名
|
||||
// if key == "" {
|
||||
// key = field.Name
|
||||
// }
|
||||
// out[key] = v.Field(i).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,8 +12,9 @@ func Test_zz(t *testing.T) {
|
||||
_ = json.Unmarshal([]byte(zz), us)
|
||||
|
||||
var err error
|
||||
us, err = MapStringToStruct[user.UserAccount](map[string]string{
|
||||
"id": "17",
|
||||
us, err = MapToStruct[user.UserAccount](map[string]string{
|
||||
"id": "17",
|
||||
"last_login_time": "2025-06-04T02:01:49.72898394+08:00",
|
||||
})
|
||||
t.Log(err)
|
||||
t.Log(us)
|
||||
|
1
go.mod
1
go.mod
@ -14,6 +14,7 @@ require (
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nats-io/nats.go v1.42.0
|
||||
golang.org/x/crypto v0.38.0
|
||||
google.golang.org/protobuf v1.33.0
|
||||
|
2
go.sum
2
go.sum
@ -57,6 +57,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
|
15
工作.txt
15
工作.txt
@ -5,13 +5,14 @@
|
||||
1.4 客户端stop关闭连接,触发服务端连接崩溃。(已修复)
|
||||
|
||||
2.编写db服
|
||||
2.1 login服向db服请求数据及向log db服写入日志。测试rpc机制。(已验证)
|
||||
2.2 db创建表时,主键不会自增,而是随机增加。
|
||||
2.3 首次创建帐号,redis没有写入帐号数据。 (已修复)
|
||||
2.4 第二次登陆,login还会走创建帐号逻辑,导致db服返回重复建号失败。 (已修复)
|
||||
2.5 redis写入数据的字段名有误。(已修复)
|
||||
2.5 清理登陆相关调试日志。
|
||||
2.6 login在创建帐号时,还需要创建user。
|
||||
2.01 login服向db服请求数据及向log db服写入日志。测试rpc机制。(已验证)
|
||||
2.02 db创建表时,主键不会自增,而是随机增加。(已修复)
|
||||
2.03 首次创建帐号,redis没有写入帐号数据。 (已修复)
|
||||
2.04 第二次登陆,login还会走创建帐号逻辑,导致db服返回重复建号失败。 (已修复)
|
||||
2.05 redis写入数据的字段名有误。(已修复)
|
||||
2.06 struct序列化到redis中时需要处理时间格式。
|
||||
2.07 清理登陆相关调试日志。
|
||||
2.08 login在创建帐号时,还需要创建user。
|
||||
|
||||
3.编写color game玩法
|
||||
3.1 服务端玩法
|
||||
|
Loading…
x
Reference in New Issue
Block a user