1、反向下单 暂时提交

This commit is contained in:
2025-07-26 09:09:09 +08:00
parent 3013486dd4
commit 771c617da4
44 changed files with 2018 additions and 614 deletions

View File

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