Compare commits
29 Commits
feature/ge
...
master
Author | SHA1 | Date | |
---|---|---|---|
b435398a94 | |||
e2c6fb3e91 | |||
59aeb88808 | |||
4196d342f7 | |||
3bb36680ad | |||
b7890f7fd0 | |||
a56574ff65 | |||
ddeb444afb | |||
fc0aa6cd5a | |||
cfdb588156 | |||
0d6d72b07e | |||
db7bf0817e | |||
e95ed61cfc | |||
51d0a05ec3 | |||
fcaa7ee1c2 | |||
5ea67a5b6c | |||
822aabaac6 | |||
dac3a32e6f | |||
118ef5d59a | |||
46cd353dd6 | |||
6dd5f7ac62 | |||
e3de2835a9 | |||
f3f064d064 | |||
ef80c6cb79 | |||
c38d16dc28 | |||
59ba4ebfa5 | |||
18351f3b29 | |||
e4cef0855e | |||
da44ae07ab |
20
any.go
20
any.go
@ -43,7 +43,18 @@ type AnyType struct {
|
|||||||
//
|
//
|
||||||
// Date : 18:21 2023/6/1
|
// Date : 18:21 2023/6/1
|
||||||
func (at *AnyType) IsNil() bool {
|
func (at *AnyType) IsNil() bool {
|
||||||
return at.data == nil
|
if at.data == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
reflectValue := reflect.ValueOf(at.data)
|
||||||
|
switch reflectValue.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Map,
|
||||||
|
reflect.Pointer, reflect.UnsafePointer,
|
||||||
|
reflect.Interface, reflect.Slice:
|
||||||
|
return reflectValue.IsNil()
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type 获取类型
|
// Type 获取类型
|
||||||
@ -97,9 +108,10 @@ func (at *AnyType) ToString() String {
|
|||||||
case consts.DataTypeSliceAny:
|
case consts.DataTypeSliceAny:
|
||||||
var val []any
|
var val []any
|
||||||
_ = serialize.JSON.Transition(at.data, &val)
|
_ = serialize.JSON.Transition(at.data, &val)
|
||||||
return String(ArrayType[any, any](val).ToString().Value)
|
return String(ArrayType[any](val).ToString().Value)
|
||||||
case consts.DataTypeMapAnyAny:
|
case consts.DataTypeMapAnyAny:
|
||||||
return String(EasyMap(at.data).ToString())
|
easyMap := EasyMap(at.data)
|
||||||
|
return String(easyMap.ToString())
|
||||||
case consts.DataTypeInt:
|
case consts.DataTypeInt:
|
||||||
return String(Int(at.data.(int64)).ToString().Value)
|
return String(Int(at.data.(int64)).ToString().Value)
|
||||||
case consts.DataTypeUint:
|
case consts.DataTypeUint:
|
||||||
@ -109,6 +121,6 @@ func (at *AnyType) ToString() String {
|
|||||||
case consts.DataTypeBool:
|
case consts.DataTypeBool:
|
||||||
return String(fmt.Sprintf("%v", at.data))
|
return String(fmt.Sprintf("%v", at.data))
|
||||||
default:
|
default:
|
||||||
return String(serialize.JSON.MarshalForString(at.data))
|
return String(serialize.JSON.MarshalForStringIgnoreError(at.data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
68
array.go
68
array.go
@ -9,7 +9,6 @@ package wrapper
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"git.zhangdeman.cn/zhangdeman/op_type"
|
"git.zhangdeman.cn/zhangdeman/op_type"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -21,8 +20,8 @@ import (
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 21:03 2023/6/11
|
// Date : 21:03 2023/6/11
|
||||||
func ArrayType[Bt op_type.BaseType, ExtractDataType op_type.BaseType](value []Bt) *Array[Bt, ExtractDataType] {
|
func ArrayType[Bt op_type.BaseType](value []Bt) *Array[Bt] {
|
||||||
at := &Array[Bt, ExtractDataType]{
|
at := &Array[Bt]{
|
||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
return at
|
return at
|
||||||
@ -33,7 +32,7 @@ func ArrayType[Bt op_type.BaseType, ExtractDataType op_type.BaseType](value []Bt
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 21:05 2023/6/11
|
// Date : 21:05 2023/6/11
|
||||||
type Array[Bt op_type.BaseType, ExtractDataType op_type.BaseType] struct {
|
type Array[Bt op_type.BaseType] struct {
|
||||||
value []Bt
|
value []Bt
|
||||||
convertErr error
|
convertErr error
|
||||||
itemType reflect.Kind // 简单list场景下, 每一项的数据类型
|
itemType reflect.Kind // 简单list场景下, 每一项的数据类型
|
||||||
@ -44,7 +43,7 @@ type Array[Bt op_type.BaseType, ExtractDataType op_type.BaseType] struct {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 21:11 2023/6/11
|
// Date : 21:11 2023/6/11
|
||||||
func (at *Array[Bt, ExtractDataType]) IsNil() bool {
|
func (at *Array[Bt]) IsNil() bool {
|
||||||
return at.value == nil
|
return at.value == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ func (at *Array[Bt, ExtractDataType]) IsNil() bool {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 11:42 2024/4/22
|
// Date : 11:42 2024/4/22
|
||||||
func (at *Array[Bt, ExtractDataType]) ToStringSlice() []string {
|
func (at *Array[Bt]) ToStringSlice() []string {
|
||||||
list := make([]string, 0)
|
list := make([]string, 0)
|
||||||
for _, item := range at.value {
|
for _, item := range at.value {
|
||||||
byteData, _ := json.Marshal(item)
|
byteData, _ := json.Marshal(item)
|
||||||
@ -67,7 +66,7 @@ func (at *Array[Bt, ExtractDataType]) ToStringSlice() []string {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 17:43 2023/6/12
|
// Date : 17:43 2023/6/12
|
||||||
func (at *Array[Bt, ExtractDataType]) Unique() []Bt {
|
func (at *Array[Bt]) Unique() []Bt {
|
||||||
result := make([]Bt, 0)
|
result := make([]Bt, 0)
|
||||||
dataTable := make(map[string]bool)
|
dataTable := make(map[string]bool)
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ func (at *Array[Bt, ExtractDataType]) Unique() []Bt {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 16:28 2023/7/31
|
// Date : 16:28 2023/7/31
|
||||||
func (at *Array[Bt, ExtractDataType]) Has(input Bt) int {
|
func (at *Array[Bt]) Has(input Bt) int {
|
||||||
for idx := 0; idx < len(at.value); idx++ {
|
for idx := 0; idx < len(at.value); idx++ {
|
||||||
if reflect.TypeOf(at.value[idx]).String() != reflect.TypeOf(input).String() {
|
if reflect.TypeOf(at.value[idx]).String() != reflect.TypeOf(input).String() {
|
||||||
// 类型不同
|
// 类型不同
|
||||||
@ -113,7 +112,7 @@ func (at *Array[Bt, ExtractDataType]) Has(input Bt) int {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 16:57 2023/9/28
|
// Date : 16:57 2023/9/28
|
||||||
func (at *Array[Bt, ExtractDataType]) ToString() StringResult {
|
func (at *Array[Bt]) ToString() StringResult {
|
||||||
if at.IsNil() {
|
if at.IsNil() {
|
||||||
return StringResult{
|
return StringResult{
|
||||||
Value: "",
|
Value: "",
|
||||||
@ -132,7 +131,7 @@ func (at *Array[Bt, ExtractDataType]) ToString() StringResult {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 17:42 2023/10/25
|
// Date : 17:42 2023/10/25
|
||||||
func (at *Array[Bt, ExtractDataType]) ToStringWithSplit(split string) StringResult {
|
func (at *Array[Bt]) ToStringWithSplit(split string) StringResult {
|
||||||
if at.IsNil() {
|
if at.IsNil() {
|
||||||
return StringResult{
|
return StringResult{
|
||||||
Value: "",
|
Value: "",
|
||||||
@ -145,46 +144,23 @@ func (at *Array[Bt, ExtractDataType]) ToStringWithSplit(split string) StringResu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractField 提取指定字段, 转换成数组
|
// ExtraField 提取[]map/[]struct 中的指定字段, 并以list形式返回
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 15:24 2024/8/6
|
// Date : 19:00 2024/10/13
|
||||||
func (at *Array[Bt, ExtractDataType]) ExtractField(fieldPath string) ([]ExtractDataType, error) {
|
func (at *Array[Bt]) ExtraField(fieldName string) String {
|
||||||
strValResult := at.ToString()
|
if at.IsNil() {
|
||||||
if nil != strValResult.Err {
|
return String("[]")
|
||||||
return make([]ExtractDataType, 0), nil
|
|
||||||
}
|
}
|
||||||
gjsonResult := gjson.Parse(strValResult.Value)
|
byteData, _ := json.Marshal(at.value)
|
||||||
if !gjsonResult.IsArray() {
|
res := make([]any, 0)
|
||||||
return make([]ExtractDataType, 0), errors.New("input value is not slice")
|
list := gjson.ParseBytes(byteData).Array()
|
||||||
|
for _, item := range list {
|
||||||
|
itemValue := item.Get(fieldName)
|
||||||
|
if itemValue.Exists() {
|
||||||
|
res = append(res, itemValue.Value())
|
||||||
}
|
}
|
||||||
arrList := gjsonResult.Array()
|
|
||||||
if len(arrList) == 0 {
|
|
||||||
return make([]ExtractDataType, 0), nil
|
|
||||||
}
|
}
|
||||||
res := make([]ExtractDataType, 0)
|
return String(ArrayType(res).ToString().Value)
|
||||||
for _, item := range arrList {
|
|
||||||
valueResult := item.Get(fieldPath)
|
|
||||||
if !valueResult.Exists() {
|
|
||||||
// 不存在
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var val ExtractDataType
|
|
||||||
if err := ConvertAssign(&val, valueResult.String()); nil != err {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res = append(res, val)
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractFieldIgnoreError 提取指定字段并忽略异常
|
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 15:28 2024/8/6
|
|
||||||
func (at *Array[Bt, ExtractDataType]) ExtractFieldIgnoreError(field string) []ExtractDataType {
|
|
||||||
res, _ := at.ExtractField(field)
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
@ -13,24 +13,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestArray_Unique(t *testing.T) {
|
func TestArray_Unique(t *testing.T) {
|
||||||
fmt.Println(ArrayType[any, any]([]any{"1", 1, 1, "1", 2, 3}).Unique())
|
fmt.Println(ArrayType([]any{"1", 1, 1, "1", 2, 3}).Unique())
|
||||||
fmt.Println(ArrayType[int, any]([]int{1, 1, 2, 3}).Unique())
|
fmt.Println(ArrayType([]int{1, 1, 2, 3}).Unique())
|
||||||
}
|
|
||||||
|
|
||||||
func TestArray_ExtractField(t *testing.T) {
|
|
||||||
testMap := []any{
|
|
||||||
map[string]any{
|
|
||||||
"age": 18,
|
|
||||||
"name": "baicha",
|
|
||||||
},
|
|
||||||
map[string]any{
|
|
||||||
"age": 20,
|
|
||||||
"name": "qinghuan",
|
|
||||||
},
|
|
||||||
map[string]any{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
fmt.Println(ArrayType[any, int](testMap).ExtractField("age"))
|
|
||||||
fmt.Println(ArrayType[any, string](testMap).ExtractField("name"))
|
|
||||||
}
|
}
|
||||||
|
118
bigint.go
Normal file
118
bigint.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Package wrapper ...
|
||||||
|
//
|
||||||
|
// Description : wrapper ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-11-19 16:33
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BigInt 是一个自定义类型,用于在JSON编码时将int64转换为字符串。
|
||||||
|
// 建议只在Output中使用它,Input时直接使用int64。
|
||||||
|
type BigInt string
|
||||||
|
|
||||||
|
var EmptyBigInt BigInt = "0"
|
||||||
|
|
||||||
|
func (f *BigInt) IsNil() bool {
|
||||||
|
if nil == f {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(*f).IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON 自定义的JSON编码逻辑。
|
||||||
|
// @implements json.Marshaler
|
||||||
|
func (f *BigInt) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf(`"%d"`, f.Int64())), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToString 返回BigId的字符串表示。
|
||||||
|
func (f *BigInt) String() string {
|
||||||
|
if f.IsNil() {
|
||||||
|
return string(EmptyBigInt)
|
||||||
|
}
|
||||||
|
return string(*f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value 在orm写入数据库时会调用它进行格式转换.
|
||||||
|
// @implements database/sql/driver.Valuer
|
||||||
|
func (f *BigInt) Value() (driver.Value, error) {
|
||||||
|
if f.IsNil() {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return f.Uint64(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 返回BigId的int64表示。
|
||||||
|
// @implements github.com/gogf/gf/v2/util/gconv.iInt64
|
||||||
|
func (f *BigInt) Int64() int64 {
|
||||||
|
if f.IsNil() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i, err := strconv.ParseInt(string(*f), 10, 64)
|
||||||
|
if err != nil || i <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 返回BigId的uint64表示。
|
||||||
|
// @implements github.com/gogf/gf/v2/util/gconv.iUint64
|
||||||
|
func (f *BigInt) Uint64() uint64 {
|
||||||
|
if f.IsNil() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint64(f.Int64())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty 判断BigId是否为空
|
||||||
|
func (f *BigInt) IsEmpty() bool {
|
||||||
|
if f.IsNil() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *f == "" || *f == EmptyBigInt || f.Int64() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON converts a json byte array of a big ID into an BigInt type.
|
||||||
|
// @implements json.Unmarshaler
|
||||||
|
func (f *BigInt) UnmarshalJSON(b []byte) error {
|
||||||
|
var (
|
||||||
|
s = string(b)
|
||||||
|
)
|
||||||
|
// 如果是null,设置为0
|
||||||
|
if s == "null" || s == "" || s == "0" {
|
||||||
|
*f = EmptyBigInt
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 如果是字符串,去掉引号
|
||||||
|
if len(b) >= 3 && b[0] == '"' && b[len(b)-1] == '"' {
|
||||||
|
s = string(b[1 : len(b)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
*f = StrToBigInt(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBigInt 将int转换为BigInt
|
||||||
|
func ToBigInt(id int64) BigInt {
|
||||||
|
if id <= 0 {
|
||||||
|
return EmptyBigInt
|
||||||
|
}
|
||||||
|
return BigInt(fmt.Sprintf("%d", id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrToBigInt 将str转换为BigInt类型
|
||||||
|
func StrToBigInt(id string) BigInt {
|
||||||
|
i, err := strconv.ParseInt(id, 10, 64)
|
||||||
|
if err != nil || i <= 0 {
|
||||||
|
return EmptyBigInt
|
||||||
|
}
|
||||||
|
return BigInt(id)
|
||||||
|
}
|
@ -7,7 +7,9 @@
|
|||||||
// Date : 2023-05-05 14:44
|
// Date : 2023-05-05 14:44
|
||||||
package wrapper
|
package wrapper
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// Int8Result ...
|
// Int8Result ...
|
||||||
//
|
//
|
||||||
|
198
dynamic_struct.go
Normal file
198
dynamic_struct.go
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
// Package wrapper ...
|
||||||
|
//
|
||||||
|
// Description : wrapper ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-08-21 16:43
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDynamic() *DynamicStruct {
|
||||||
|
return &DynamicStruct{
|
||||||
|
structFieldList: make([]reflect.StructField, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DynamicStruct 动态生成数据结构
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:48 2024/8/21
|
||||||
|
type DynamicStruct struct {
|
||||||
|
structFieldList []reflect.StructField // 结构体字段列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInt 添加int字段统一Int64
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:50 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddInt(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(int64(0)),
|
||||||
|
Tag: reflect.StructTag(fmt.Sprintf(`json:"%v"`, fieldTag)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint 添加uint字段, 统一 uint64
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:50 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddUint(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(uint64(0)),
|
||||||
|
Tag: reflect.StructTag(fieldTag),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddString 添加字符串字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:49 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddString(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(""),
|
||||||
|
Tag: reflect.StructTag(fmt.Sprintf(`json:"%v"`, fieldTag)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBool 添加bool字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:49 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddBool(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(true),
|
||||||
|
Tag: reflect.StructTag(fmt.Sprintf(`json:"%v"`, fieldTag)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFloat 添加float字段, 统一 float64
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:48 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddFloat(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(float64(0)),
|
||||||
|
Tag: reflect.StructTag(fmt.Sprintf(`json:"%v"`, fieldTag)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSlice 添加slice
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:47 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddSlice(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf([]any{}),
|
||||||
|
Tag: reflect.StructTag(fieldTag),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMap 添加map字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:43 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddMap(fieldName string, fieldTag string, pkgPath string) {
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(map[string]any{}),
|
||||||
|
Tag: reflect.StructTag(fieldTag),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAny 添加任意类型字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:52 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddAny(fieldName string, fieldTag string, pkgPath string, value any) {
|
||||||
|
if nil == value {
|
||||||
|
// 不能是空指针
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ds.AddStructField(reflect.StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
Type: reflect.TypeOf(value),
|
||||||
|
Tag: reflect.StructTag(fieldTag),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddStructField 添加结构体字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:42 2024/8/21
|
||||||
|
func (ds *DynamicStruct) AddStructField(field reflect.StructField) {
|
||||||
|
if field.Tag == "" {
|
||||||
|
field.Tag = reflect.StructTag(fmt.Sprintf(`json:"%v"`, field.Name))
|
||||||
|
}
|
||||||
|
field.Name = String(field.Name).SnakeCaseToCamel() // 转成大驼峰, 保证对外可访问
|
||||||
|
ds.structFieldList = append(ds.structFieldList, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStructType 获取结构体的类型
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:58 2024/8/21
|
||||||
|
func (ds *DynamicStruct) GetStructType() reflect.Type {
|
||||||
|
return reflect.StructOf(ds.structFieldList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStructDefaultValue 获取结构体的值, 并采用对应类型默认值填充相关字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:56 2024/8/21
|
||||||
|
func (ds *DynamicStruct) ToStructDefaultValue() any {
|
||||||
|
defer ds.Clear()
|
||||||
|
defaultValue := reflect.New(ds.GetStructType()).Elem().Interface()
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStructDefaultSliceValue 自动生成结构体列表
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:04 2024/8/21
|
||||||
|
func (ds *DynamicStruct) ToStructDefaultSliceValue() any {
|
||||||
|
defer ds.Clear()
|
||||||
|
tSlice := reflect.MakeSlice(reflect.SliceOf(ds.GetStructType()), 0, 0)
|
||||||
|
return reflect.New(tSlice.Type()).Elem().Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear 清理
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 17:08 2024/8/21
|
||||||
|
func (ds *DynamicStruct) Clear() {
|
||||||
|
// 清理掉已设置的字段, 不然实例复用会互相影响
|
||||||
|
ds.structFieldList = make([]reflect.StructField, 0)
|
||||||
|
}
|
27
dynamic_struct_test.go
Normal file
27
dynamic_struct_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Package wrapper ...
|
||||||
|
//
|
||||||
|
// Description : wrapper ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-08-21 17:56
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewDynamic(t *testing.T) {
|
||||||
|
instance := NewDynamic()
|
||||||
|
instance.AddInt("Age", "age", "")
|
||||||
|
instance.AddString("Name", "name", "")
|
||||||
|
defaultVal := instance.ToStructDefaultValue()
|
||||||
|
testMap := map[string]any{
|
||||||
|
"name": "白茶",
|
||||||
|
"age": 18,
|
||||||
|
}
|
||||||
|
_ = mapstructure.Decode(testMap, &defaultVal)
|
||||||
|
fmt.Println(defaultVal)
|
||||||
|
}
|
86
easymap.go
Normal file
86
easymap.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Package wrapper ...
|
||||||
|
//
|
||||||
|
// Description : wrapper ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2023-08-10 15:01
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EasyMap ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:02 2023/8/10
|
||||||
|
func EasyMap(mapData any) Map {
|
||||||
|
m, _ := EasyMapWithError(mapData)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// EasyMapWithError 转换map,并带上转换的异常
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:06 2023/8/10
|
||||||
|
func EasyMapWithError(mapData any) (Map, error) {
|
||||||
|
if nil == mapData {
|
||||||
|
return map[string]any{}, nil
|
||||||
|
}
|
||||||
|
reflectType := reflect.TypeOf(mapData)
|
||||||
|
if reflectType.Kind() != reflect.Map {
|
||||||
|
mapFormatData := make(map[string]any)
|
||||||
|
if err := serialize.JSON.UnmarshalWithNumber(serialize.JSON.MarshalForByteIgnoreError(mapData), &mapFormatData); nil != err {
|
||||||
|
return mapFormatData, errors.New("input data type is " + reflectType.String() + ", can not convert to map")
|
||||||
|
}
|
||||||
|
return mapFormatData, nil
|
||||||
|
}
|
||||||
|
m := Map(map[string]any{})
|
||||||
|
reflectValue := reflect.ValueOf(mapData).MapRange()
|
||||||
|
for reflectValue.Next() {
|
||||||
|
// 循环提取相关值
|
||||||
|
_ = m.Set(reflectValue.Key().String(), reflectValue.Value().Interface())
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EasyMapFromStruct 从struct转map
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:11 2023/8/10
|
||||||
|
func EasyMapFromStruct(data any) Map {
|
||||||
|
byteData, _ := json.Marshal(data)
|
||||||
|
return EasyMapFromByte(byteData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EasyMapFromString 从string转为Map
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:12 2023/8/10
|
||||||
|
func EasyMapFromString(data string) Map {
|
||||||
|
return EasyMapFromByte([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EasyMapFromByte 从字节数组转为Map
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 16:12 2023/8/10
|
||||||
|
func EasyMapFromByte(data []byte) Map {
|
||||||
|
res := Map(map[string]any{})
|
||||||
|
jsonRes := gjson.Parse(string(data))
|
||||||
|
jsonRes.ForEach(func(key, value gjson.Result) bool {
|
||||||
|
_ = res.Set(key.String(), value.Value())
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
12
go.mod
12
go.mod
@ -5,18 +5,18 @@ go 1.21
|
|||||||
toolchain go1.21.4
|
toolchain go1.21.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20241104082108-0f97a870bbc3
|
||||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211
|
|
||||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0
|
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd
|
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241104092308-ecb02113459e
|
||||||
|
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e
|
||||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
|
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/spaolacci/murmur3 v1.1.0
|
github.com/spaolacci/murmur3 v1.1.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/tidwall/gjson v1.17.3
|
github.com/tidwall/gjson v1.18.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
|
|
||||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
|
40
go.sum
40
go.sum
@ -1,33 +1,11 @@
|
|||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240104123641-b3f23974e5d6 h1:ytpXTP3oxp480BAZQoOzqlBP4XP73NcpMplZ1/fA1lQ=
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20241104082108-0f97a870bbc3 h1:BiAlBJ+DuRs/xD7nDQD2JT8Oc+V+0Uwt36qZwdXGvzI=
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240104123641-b3f23974e5d6/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20241104082108-0f97a870bbc3/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240419080457-9d9562469008 h1:6z99+X/B/G9sCZ+aTLYGWk3YLVVODzevA4wjWj9jvq0=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240419080457-9d9562469008/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240501142503-e31a270e50cc h1:kPz9xiUVruM8kwbUUVpxyCTX8pGgyKt60K5zX77oyC4=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240501142503-e31a270e50cc/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240608124542-4d97bd80dc68 h1:AaWKU0bKHnNot24OMhaOCBKtpfhz4o05DKHrRFgYd8M=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240608124542-4d97bd80dc68/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240612081722-31c64d4d4ce7 h1:QR8vMXOTy0NFKdodsGKA4gTNHJMfob3yRFYMXrZj7ek=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240612081722-31c64d4d4ce7/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyzYbZullK0aTHVASHl3UeoVr8IgytQZsuyv+yEM=
|
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
|
||||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240130062251-a87a97b0e8d4 h1:93JYY8JLbFcrlq37q/uKyxs2r2e3modsjvfSbnZQ/UI=
|
|
||||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240130062251-a87a97b0e8d4/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U=
|
|
||||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0=
|
|
||||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U=
|
|
||||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y=
|
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y=
|
||||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
|
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240110090803-399e964daa0c h1:k7VCn9GfRGTilvdF/TcTFVMDBfKLe3VeGAtMTiDSnS0=
|
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241104092308-ecb02113459e h1:A045F67AMSqFKGD9kk2uLa+6c/zpmW8vjjSRmSsdjPs=
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240110090803-399e964daa0c/go.mod h1:w7kG4zyTJ1uPFaTWhze+OQuaUBINT2XnDxpyiM6ctc0=
|
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241104092308-ecb02113459e/go.mod h1:XqgER4jDYwskFgj2riJ9XptIjzgYWubY+Zq8iB2WkY0=
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240325080031-1f58204e8687 h1:uQcGqdzi4UdpZlp4f4FUPeBqoygP58pEKJkmN3ROsE0=
|
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240325080031-1f58204e8687/go.mod h1:gf7SW2TXATgux8pfdFedMkXWv2515OtIIM/5c4atkFw=
|
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd h1:2Y37waOVCmVvx0Rp8VGEptE2/2JVMImtxB4dKKDk/3w=
|
|
||||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd/go.mod h1:6+7whkCmb4sJDIfH3HxNuXRveaM0gCCNWd2uXZqNtIE=
|
|
||||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253 h1:GO3oZa5a2sqwAzGcLDJtQzmshSWRmoP7IDS8bwFqvC4=
|
|
||||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20231227095334-7eb5cdbf9253/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
|
||||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
|
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
|
||||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
||||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
|
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
|
||||||
@ -38,16 +16,18 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
|||||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
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/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ=
|
github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ=
|
||||||
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
|
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||||
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
299
map.go
299
map.go
@ -4,92 +4,283 @@
|
|||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 2023-08-10 15:01
|
// Date : 2024-11-06 18:27
|
||||||
package wrapper
|
package wrapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"git.zhangdeman.cn/zhangdeman/easymap"
|
|
||||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EasyMap ...
|
var mapLock = &sync.RWMutex{}
|
||||||
|
|
||||||
|
type Map map[string]any
|
||||||
|
|
||||||
|
func (m Map) lock() {
|
||||||
|
mapLock.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Map) unlock() {
|
||||||
|
mapLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Map) rLock() {
|
||||||
|
mapLock.RLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Map) rUnlock() {
|
||||||
|
mapLock.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exist key是否存在
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 15:02 2023/8/10
|
// Date : 18:34 2024/11/6
|
||||||
func EasyMap(mapData any) Map {
|
func (m Map) Exist(key string) bool {
|
||||||
m, _ := EasyMapWithError(mapData)
|
if m.IsNil() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
m.rLock()
|
||||||
|
defer m.rUnlock()
|
||||||
|
_, exist := m[key]
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 设置map的值, 字段如果已存在, 会覆盖
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - field : 摇摆存的字段
|
||||||
|
// - value : 字段对应的值
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:16 2024/11/19
|
||||||
|
func (m Map) Set(field string, value any) error {
|
||||||
|
if m.IsNil() {
|
||||||
|
return errors.New("Map is nil")
|
||||||
|
}
|
||||||
|
m.lock()
|
||||||
|
defer m.unlock()
|
||||||
|
m[field] = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Del 删除指定的字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:21 2024/11/19
|
||||||
|
func (m Map) Del(field string) {
|
||||||
|
if m.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.lock()
|
||||||
|
defer m.unlock()
|
||||||
|
delete(m, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil 判断map是否为nil
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:22 2024/11/19
|
||||||
|
func (m Map) IsNil() bool {
|
||||||
|
if nil == m {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(m).IsNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 14:41 2024/11/19
|
||||||
|
func (m Map) Get(field string) (any, error) {
|
||||||
|
if m.IsNil() {
|
||||||
|
return nil, errors.New("map is nil")
|
||||||
|
}
|
||||||
|
m.rLock()
|
||||||
|
defer m.rUnlock()
|
||||||
|
val, exist := m[field]
|
||||||
|
if !exist {
|
||||||
|
return nil, errors.New(field + " : field not found")
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefault 获取指定字段, 不存在则设置默认值
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - field : 要读取的字段
|
||||||
|
// - defaultValue : 字段不存在返回的默认值
|
||||||
|
// - allowNil : 字段存在, 但是值为Nil, 是否是一个合法值
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 14:59 2024/11/19
|
||||||
|
func (m Map) GetDefault(field string, defaultValue any, allowNil bool) any {
|
||||||
|
if m.IsNil() {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
m.rLock()
|
||||||
|
defer m.rUnlock()
|
||||||
|
val, exist := m[field]
|
||||||
|
if !exist {
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
if nil == val {
|
||||||
|
if allowNil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value 获取数据值
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 19:39 2024/11/6
|
||||||
|
func (m Map) Value() map[string]any {
|
||||||
|
if m.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// EasyMapWithError 转换map,并带上转换的异常
|
// Clone 克隆数据
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 15:06 2023/8/10
|
// Date : 19:40 2024/11/6
|
||||||
func EasyMapWithError(mapData any) (Map, error) {
|
func (m Map) Clone() Map {
|
||||||
if nil == mapData {
|
newData := map[string]any{}
|
||||||
return easymap.NewNormal(), nil
|
if m.IsNil() {
|
||||||
|
return newData
|
||||||
}
|
}
|
||||||
m := easymap.NewNormal()
|
m.rLock()
|
||||||
reflectType := reflect.TypeOf(mapData)
|
defer m.rUnlock()
|
||||||
if reflectType.Kind() != reflect.Map {
|
mapValue := m.Value()
|
||||||
mapFormatData := make(map[string]any)
|
for k, v := range mapValue {
|
||||||
if err := serialize.JSON.UnmarshalWithNumber(serialize.JSON.MarshalForByte(mapData), &mapFormatData); nil != err {
|
newData[k] = v
|
||||||
return m, errors.New("input data type is " + reflectType.String() + ", can not convert to map")
|
|
||||||
}
|
}
|
||||||
mapData = mapFormatData
|
return newData
|
||||||
}
|
|
||||||
|
|
||||||
reflectValue := reflect.ValueOf(mapData).MapRange()
|
|
||||||
for reflectValue.Next() {
|
|
||||||
// 循环提取相关值
|
|
||||||
m.Set(reflectValue.Key().Interface(), reflectValue.Value().Interface())
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EasyMapFromStruct 从struct转map
|
// Filter 过滤指定字段
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - fieldList : 要保留的字段列表
|
||||||
|
// - ignoreNotFound : 指定字段不存在是否忽略,如不忽略, 字段不存在, 将会报错
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 16:11 2023/8/10
|
// Date : 14:40 2024/11/19
|
||||||
func EasyMapFromStruct(data any) Map {
|
func (m Map) Filter(fieldList []string, ignoreNotFound bool) (map[string]any, error) {
|
||||||
byteData, _ := json.Marshal(data)
|
res := make(map[string]any)
|
||||||
return EasyMapFromByte(byteData)
|
for _, itemField := range fieldList {
|
||||||
|
if val, err := m.Get(itemField); err == nil {
|
||||||
|
res[itemField] = val
|
||||||
|
} else {
|
||||||
|
if !ignoreNotFound {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EasyMapFromString 从string转为Map
|
// FilterDefault 过滤指定字段, 字段不存储在则用默认值填充
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - fieldMap : 查询字段表, key为要查询的字段, value为 字段不存在时返回的默认值
|
||||||
|
// - allowNil : 字段存在, 三只值为你来是否是一个合法值
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 16:12 2023/8/10
|
// Date : 15:07 2024/11/19
|
||||||
func EasyMapFromString(data string) Map {
|
func (m Map) FilterDefault(fieldMap map[string]any, allowNil bool) map[string]any {
|
||||||
return EasyMapFromByte([]byte(data))
|
res := make(map[string]any)
|
||||||
}
|
for itemField, fieldDefaultValue := range fieldMap {
|
||||||
|
res[itemField] = m.GetDefault(itemField, fieldDefaultValue, allowNil)
|
||||||
// EasyMapFromByte 从字节数组转为Map
|
}
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 16:12 2023/8/10
|
|
||||||
func EasyMapFromByte(data []byte) Map {
|
|
||||||
res := easymap.NewNormal()
|
|
||||||
jsonRes := gjson.Parse(string(data))
|
|
||||||
jsonRes.ForEach(func(key, value gjson.Result) bool {
|
|
||||||
res.Set(key.Value(), value.Value())
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map ...
|
// MarshalJSON Map序列化
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 15:14 2023/8/10
|
// Date : 15:35 2024/11/19
|
||||||
type Map easymap.EasyMap
|
func (m Map) MarshalJSON() ([]byte, error) {
|
||||||
|
mapData := m.Value()
|
||||||
|
if nil == mapData {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return serialize.JSON.MarshalForByte(mapData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToString 序列化成字符串
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 14:24 2024/11/21
|
||||||
|
func (m Map) ToString() string {
|
||||||
|
byteData, _ := m.MarshalJSON()
|
||||||
|
return string(byteData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetString 获取字符串结果
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - field : 要查找的字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:15 2024/11/21
|
||||||
|
func (m Map) GetString(field string) (string, error) {
|
||||||
|
val, err := m.Get(field)
|
||||||
|
if nil != err {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return AnyDataType(val).ToString().Value(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64 获取Int64值
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - field : 要查找的字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:18 2024/11/21
|
||||||
|
func (m Map) GetInt64(field string) (int64, error) {
|
||||||
|
val, err := m.Get(field)
|
||||||
|
if nil != err {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
int64Res := AnyDataType(val).ToString().ToInt64()
|
||||||
|
return int64Res.Value, int64Res.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFloat64 获取float64值
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
// - field : 要查找的字段
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 15:18 2024/11/21
|
||||||
|
func (m Map) GetFloat64(field string) (float64, error) {
|
||||||
|
val, err := m.Get(field)
|
||||||
|
if nil != err {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
float64Res := AnyDataType(val).ToString().ToFloat64()
|
||||||
|
return float64Res.Value, float64Res.Err
|
||||||
|
}
|
||||||
|
47
map_test.go
Normal file
47
map_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Package wrapper ...
|
||||||
|
//
|
||||||
|
// Description : wrapper ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2024-11-06 18:37
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMap_Exist(t *testing.T) {
|
||||||
|
testData := Map(map[string]any{
|
||||||
|
"name": "zhang",
|
||||||
|
})
|
||||||
|
fmt.Println(testData.Exist("name"))
|
||||||
|
fmt.Println(testData.Exist("age"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_IsNil(t *testing.T) {
|
||||||
|
var (
|
||||||
|
m Map
|
||||||
|
m1 *Map
|
||||||
|
)
|
||||||
|
fmt.Println(m.Set("a", 1))
|
||||||
|
fmt.Println(m.IsNil(), m1.IsNil())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap_IsMasher(t *testing.T) {
|
||||||
|
var (
|
||||||
|
m Map
|
||||||
|
m1 = Map(map[string]any{
|
||||||
|
"a": 1,
|
||||||
|
"b": m,
|
||||||
|
"c": Map(map[string]any{
|
||||||
|
"name": "de",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
d, err := m.MarshalJSON()
|
||||||
|
fmt.Println(string(d), err)
|
||||||
|
d, err = m1.MarshalJSON()
|
||||||
|
fmt.Println(string(d), err)
|
||||||
|
}
|
@ -71,7 +71,10 @@ func (ot *ObjectType) IsValid() bool {
|
|||||||
//
|
//
|
||||||
// Date : 18:50 2023/6/1
|
// Date : 18:50 2023/6/1
|
||||||
func (ot *ObjectType) IsNil() bool {
|
func (ot *ObjectType) IsNil() bool {
|
||||||
return ot.source == nil
|
if ot.source == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(ot.source).IsNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToString 转字符串
|
// ToString 转字符串
|
||||||
|
@ -136,21 +136,21 @@ func DefaultDiffFunc(field string, inputVal wrapper.Map, storageVal wrapper.Map,
|
|||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
inputFieldVal any
|
inputFieldVal any
|
||||||
inputFieldValExist bool
|
inputFieldValExist error
|
||||||
storageFieldVal any
|
storageFieldVal any
|
||||||
storageFieldValExist bool
|
storageFieldValExist error
|
||||||
)
|
)
|
||||||
|
|
||||||
inputFieldVal, inputFieldValExist = inputVal.Get(field)
|
inputFieldVal, inputFieldValExist = inputVal.Get(field)
|
||||||
storageFieldVal, storageFieldValExist = storageVal.Get(field)
|
storageFieldVal, storageFieldValExist = storageVal.Get(field)
|
||||||
// 字段在输入数据和存储数据中均不存在
|
// 字段在输入数据和存储数据中均不存在
|
||||||
if !inputFieldValExist && !storageFieldValExist {
|
if nil != inputFieldValExist && nil != storageFieldValExist {
|
||||||
// 输入和存储都没这个字段
|
// 输入和存储都没这个字段
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断输入字段是否存在
|
// 判断输入字段是否存在
|
||||||
if !inputFieldValExist {
|
if nil != inputFieldValExist {
|
||||||
if option.IgnoreNotFoundField {
|
if option.IgnoreNotFoundField {
|
||||||
// 忽略不存在的字段
|
// 忽略不存在的字段
|
||||||
return result
|
return result
|
||||||
@ -162,7 +162,7 @@ func DefaultDiffFunc(field string, inputVal wrapper.Map, storageVal wrapper.Map,
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
// 判断存储字段是否存在
|
// 判断存储字段是否存在
|
||||||
if !storageFieldValExist {
|
if nil != storageFieldValExist {
|
||||||
result.IsSame = false
|
result.IsSame = false
|
||||||
result.DiffReason = DiffReasonStorageFieldNotFound
|
result.DiffReason = DiffReasonStorageFieldNotFound
|
||||||
result.NewVal = inputFieldVal
|
result.NewVal = inputFieldVal
|
||||||
|
Loading…
Reference in New Issue
Block a user