1、反向下单 暂时提交
This commit is contained in:
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// RedisHelper 结构体封装了 Redis 客户端及上下文
|
||||
@ -460,44 +461,85 @@ func (r *RedisHelper) SetNX(key string, value interface{}, expiration time.Durat
|
||||
return result == "OK", nil
|
||||
}
|
||||
|
||||
func getFieldsFromStruct(obj interface{}) map[string]interface{} {
|
||||
// SetHashWithTags 改进版:支持 struct 或 map 输入
|
||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||
var fields map[string]interface{}
|
||||
var err error
|
||||
|
||||
// 1. 优先检查 obj 是否已经是一个 map[string]interface{}
|
||||
if m, ok := obj.(map[string]interface{}); ok {
|
||||
fields = m // 如果是,直接使用这个 map
|
||||
} else {
|
||||
// 2. 如果不是 map,则假设它是一个 struct,并尝试从 struct 中获取字段
|
||||
fields, err = getFieldsFromStruct(obj) // getFieldsFromStruct 现在需要返回 error
|
||||
if err != nil {
|
||||
return fmt.Errorf("从结构体获取字段失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := r.client.HSet(r.ctx, key, fields)
|
||||
return cmd.Err()
|
||||
}
|
||||
|
||||
// getFieldsFromStruct 改进版:处理指针并进行类型检查,返回 error
|
||||
func getFieldsFromStruct(obj interface{}) (map[string]interface{}, error) {
|
||||
fields := make(map[string]interface{})
|
||||
val := reflect.ValueOf(obj)
|
||||
typ := reflect.TypeOf(obj)
|
||||
|
||||
// 如果 obj 是指针,则解引用
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
// 确保我们正在处理的是一个结构体
|
||||
if val.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("期望一个结构体或结构体指针,但得到的是 %s", val.Kind())
|
||||
}
|
||||
|
||||
typ := val.Type() // 在解引用后获取类型
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
tag := field.Tag.Get("redis")
|
||||
tag := field.Tag.Get("redis") // 获取 redis tag
|
||||
if tag != "" {
|
||||
fieldVal := val.Field(i)
|
||||
if fieldVal.Kind() == reflect.Slice || fieldVal.Kind() == reflect.Map {
|
||||
// 检查字段是否可导出,不可导出的字段无法直接通过 Interface() 访问
|
||||
if !fieldVal.CanInterface() {
|
||||
continue // 跳过不可导出字段
|
||||
}
|
||||
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.Slice, reflect.Map:
|
||||
// 处理切片或映射类型
|
||||
// 对于切片,使用索引作为字段名
|
||||
if fieldVal.Kind() == reflect.Slice {
|
||||
for j := 0; j < fieldVal.Len(); j++ {
|
||||
elem := fieldVal.Index(j).Interface()
|
||||
fields[fmt.Sprintf("%s_%d", tag, j)] = elem
|
||||
}
|
||||
} else if fieldVal.Kind() == reflect.Map {
|
||||
// 对于映射,使用键作为字段名
|
||||
for _, key := range fieldVal.MapKeys() {
|
||||
elem := fieldVal.MapIndex(key).Interface()
|
||||
fields[fmt.Sprintf("%s_%v", tag, key.Interface())] = elem
|
||||
}
|
||||
}
|
||||
} else {
|
||||
case reflect.Struct:
|
||||
if fieldVal.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||
if decVal, ok := fieldVal.Interface().(decimal.Decimal); ok {
|
||||
// 将 decimal.Decimal 直接转换为字符串来保留精度
|
||||
fields[tag] = decVal.String()
|
||||
} else {
|
||||
// 理论上不应该发生,但作为回退
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
} else {
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
default:
|
||||
fields[tag] = fieldVal.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (r *RedisHelper) SetHashWithTags(key string, obj interface{}) error {
|
||||
fields := getFieldsFromStruct(obj)
|
||||
_, err := r.client.HSet(r.ctx, key, fields).Result()
|
||||
return err
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// HSetField 设置哈希中的一个字段
|
||||
@ -543,6 +585,82 @@ func (r *RedisHelper) HGetAllFields(key string) (map[string]string, error) {
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// HGetAsObject 获取哈希中所有字段的值并反序列化为对象
|
||||
func (r *RedisHelper) HGetAsObject(key string, out interface{}) error {
|
||||
data, err := r.HGetAllFields(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(out)
|
||||
if val.Kind() != reflect.Ptr || val.IsNil() {
|
||||
return fmt.Errorf("output must be a non-nil pointer to a struct")
|
||||
}
|
||||
val = val.Elem()
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
tag := field.Tag.Get("redis")
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
strVal, ok := data[tag]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldVal := val.Field(i)
|
||||
if !fieldVal.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.String:
|
||||
fieldVal.SetString(strVal)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil {
|
||||
fieldVal.SetInt(intVal)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if uintVal, err := strconv.ParseUint(strVal, 10, 64); err == nil {
|
||||
fieldVal.SetUint(uintVal)
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if floatVal, err := strconv.ParseFloat(strVal, 64); err == nil {
|
||||
fieldVal.SetFloat(floatVal)
|
||||
}
|
||||
case reflect.Bool:
|
||||
if boolVal, err := strconv.ParseBool(strVal); err == nil {
|
||||
fieldVal.SetBool(boolVal)
|
||||
}
|
||||
case reflect.Struct:
|
||||
// 针对 time.Time 特别处理
|
||||
if fieldVal.Type() == reflect.TypeOf(time.Time{}) {
|
||||
if t, err := time.Parse(time.RFC3339, strVal); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(t))
|
||||
}
|
||||
} else if fieldVal.Type() == reflect.TypeOf(decimal.Decimal{}) {
|
||||
if t, err := decimal.NewFromString(strVal); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(t))
|
||||
}
|
||||
} else {
|
||||
// 其他 struct 尝试用 json 反序列化
|
||||
ptr := reflect.New(fieldVal.Type()).Interface()
|
||||
if err := sonic.Unmarshal([]byte(strVal), ptr); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(ptr).Elem())
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Map:
|
||||
ptr := reflect.New(fieldVal.Type()).Interface()
|
||||
if err := sonic.Unmarshal([]byte(strVal), ptr); err == nil {
|
||||
fieldVal.Set(reflect.ValueOf(ptr).Elem())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HDelField 删除哈希中的某个字段
|
||||
func (r *RedisHelper) HDelField(key, field string) error {
|
||||
_, err := r.client.HDel(r.ctx, key, field).Result()
|
||||
|
||||
Reference in New Issue
Block a user