mirror of
https://gitee.com/XM-GO/PandaX.git
synced 2026-05-07 12:31:26 +08:00
43
kit/utils/ddm.go
Normal file
43
kit/utils/ddm.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package utils
|
||||
|
||||
/**
|
||||
* @Description 添加qq群467890197 交流学习
|
||||
* @Author 熊猫
|
||||
* @Date 2022/1/13 17:16
|
||||
**/
|
||||
|
||||
func DdmKey(data string) string {
|
||||
if len(data) < 6 {
|
||||
return data
|
||||
}
|
||||
return data[:3] + "****" + data[len(data)-3:]
|
||||
}
|
||||
|
||||
func IsDdmKey(data string) bool {
|
||||
if len(data) > 6 && data[3:len(data)-3] == "****" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DdmMail(data string) string {
|
||||
return data[:3] + "****" + data[len(data)-8:]
|
||||
}
|
||||
|
||||
func ISDdmMail(data string) bool {
|
||||
if len(data) > 11 && data[3:len(data)-8] == "****" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DdmPassword(data string) string {
|
||||
return "******"
|
||||
}
|
||||
|
||||
func IsDdmPassword(data string) bool {
|
||||
if data == "******" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
26
kit/utils/ddm_test.go
Normal file
26
kit/utils/ddm_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
/**
|
||||
* @Description 添加qq群467890197 交流学习
|
||||
* @Author 熊猫
|
||||
* @Date 2022/1/17 10:01
|
||||
**/
|
||||
|
||||
func TestDdmKey(t *testing.T) {
|
||||
aa := "fsdfsf535343sdfsdf3"
|
||||
bb := "23423423@qq.com"
|
||||
|
||||
key := DdmKey(aa)
|
||||
t.Log(key)
|
||||
ddmKey := IsDdmKey(key)
|
||||
t.Log(ddmKey)
|
||||
|
||||
mail := DdmMail(bb)
|
||||
t.Log(mail)
|
||||
ismail := ISDdmMail(mail)
|
||||
t.Log(ismail)
|
||||
}
|
||||
46
kit/utils/excel.go
Normal file
46
kit/utils/excel.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func ExportExcel(head []string, datas [][]any, filePath string) error {
|
||||
excel := excelize.NewFile()
|
||||
excel.SetSheetRow("Sheet1", "A1", &head)
|
||||
for i, data := range datas {
|
||||
axis := fmt.Sprintf("A%d", i+2)
|
||||
excel.SetSheetRow("Sheet1", axis, &data)
|
||||
}
|
||||
excel.SaveAs(filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFileName(path, filename string) string {
|
||||
return path + filename
|
||||
}
|
||||
|
||||
func InterfaceToExcel(data any, fileName string) {
|
||||
heads := make([]string, 0)
|
||||
datas := make([][]any, 0)
|
||||
v := reflect.ValueOf(data)
|
||||
max := int64(v.Len())
|
||||
for i := 0; i < int(max); i++ {
|
||||
u := v.Index(i).Interface()
|
||||
var key = reflect.TypeOf(u)
|
||||
var val = reflect.ValueOf(u)
|
||||
num := key.NumField()
|
||||
if i == 0 {
|
||||
for i := 0; i < num; i++ {
|
||||
heads = append(heads, key.Field(i).Name)
|
||||
}
|
||||
}
|
||||
field := make([]any, 0)
|
||||
for i := 0; i < num; i++ {
|
||||
field = append(field, val.Field(i).Interface())
|
||||
}
|
||||
datas = append(datas, field)
|
||||
}
|
||||
ExportExcel(heads, datas, fileName)
|
||||
}
|
||||
19
kit/utils/excel_test.go
Normal file
19
kit/utils/excel_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Age int64 `json:"age"`
|
||||
}
|
||||
|
||||
func TestExportExcel(t *testing.T) {
|
||||
us := make([]User, 0)
|
||||
us = append(us, User{Name: "张三", Age: 12})
|
||||
us = append(us, User{Name: "李四", Age: 23})
|
||||
name := GetFileName("./", "字典")
|
||||
t.Log(name)
|
||||
InterfaceToExcel(us, name)
|
||||
}
|
||||
26
kit/utils/float_to_f.go
Normal file
26
kit/utils/float_to_f.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ParseFloat2F(value float64) float64 {
|
||||
newValue, err := strconv.ParseFloat(fmt.Sprintf("%.2f", value), 64)
|
||||
if err != nil {
|
||||
fmt.Println("保留2位小数, 转换float出错")
|
||||
return value
|
||||
}
|
||||
return newValue
|
||||
|
||||
}
|
||||
|
||||
func ParseStringToInt64(value string) int64 {
|
||||
|
||||
newValue, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println("string转换int64出错")
|
||||
return 0
|
||||
}
|
||||
return newValue
|
||||
}
|
||||
0
kit/utils/float_to_f.go.go
Normal file
0
kit/utils/float_to_f.go.go
Normal file
53
kit/utils/ip.go
Normal file
53
kit/utils/ip.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"pandax/kit/httpclient"
|
||||
)
|
||||
|
||||
const ipurl = "https://ip.cn/api/index"
|
||||
|
||||
const UNKNOWN = "XX XX"
|
||||
|
||||
// GetRealAddressByIP 获取真实地址
|
||||
func GetRealAddressByIP(ip string) string {
|
||||
if ip == "127.0.0.1" || ip == "localhost" {
|
||||
return "内部IP"
|
||||
}
|
||||
url := fmt.Sprintf("%s?ip=%s&type=1", ipurl, ip)
|
||||
|
||||
res := httpclient.NewRequest(url).Get()
|
||||
if res == nil || res.StatusCode != 200 {
|
||||
return UNKNOWN
|
||||
}
|
||||
toMap, err := res.BodyToMap()
|
||||
if err != nil {
|
||||
return UNKNOWN
|
||||
}
|
||||
return toMap["address"].(string)
|
||||
}
|
||||
|
||||
// 获取局域网ip地址
|
||||
func GetLocaHonst() string {
|
||||
netInterfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
fmt.Println("net.Interfaces failed, err:", err.Error())
|
||||
}
|
||||
|
||||
for i := 0; i < len(netInterfaces); i++ {
|
||||
if (netInterfaces[i].Flags & net.FlagUp) != 0 {
|
||||
addrs, _ := netInterfaces[i].Addrs()
|
||||
|
||||
for _, address := range addrs {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return ""
|
||||
}
|
||||
14
kit/utils/json_utils.go
Normal file
14
kit/utils/json_utils.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func Json2Map(jsonStr string) map[string]any {
|
||||
var res map[string]any
|
||||
if jsonStr == "" {
|
||||
return res
|
||||
}
|
||||
_ = json.Unmarshal([]byte(jsonStr), &res)
|
||||
return res
|
||||
}
|
||||
42
kit/utils/regexp.go
Normal file
42
kit/utils/regexp.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
regexMu = sync.RWMutex{}
|
||||
// Cache for regex object.
|
||||
// Note that:
|
||||
// 1. It uses sync.RWMutex ensuring the concurrent safety.
|
||||
// 2. There's no expiring logic for this map.
|
||||
regexMap = make(map[string]*regexp.Regexp)
|
||||
)
|
||||
|
||||
// getRegexp returns *regexp.Regexp object with given `pattern`.
|
||||
// It uses cache to enhance the performance for compiling regular expression pattern,
|
||||
// which means, it will return the same *regexp.Regexp object with the same regular
|
||||
// expression pattern.
|
||||
//
|
||||
// It is concurrent-safe for multiple goroutines.
|
||||
func GetRegexp(pattern string) (regex *regexp.Regexp, err error) {
|
||||
// Retrieve the regular expression object using reading lock.
|
||||
regexMu.RLock()
|
||||
regex = regexMap[pattern]
|
||||
regexMu.RUnlock()
|
||||
if regex != nil {
|
||||
return
|
||||
}
|
||||
// If it does not exist in the cache,
|
||||
// it compiles the pattern and creates one.
|
||||
regex, err = regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Cache the result object using writing lock.
|
||||
regexMu.Lock()
|
||||
regexMap[pattern] = regex
|
||||
regexMu.Unlock()
|
||||
return
|
||||
}
|
||||
156
kit/utils/str_utils.go
Normal file
156
kit/utils/str_utils.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/kakuilan/kgo"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func UnicodeIndex(str, substr string) int {
|
||||
// 子串在字符串的字节位置
|
||||
result := strings.Index(str, substr)
|
||||
if result >= 0 {
|
||||
// 获得子串之前的字符串并转换成[]byte
|
||||
prefix := []byte(str)[0:result]
|
||||
// 将子串之前的字符串转换成[]rune
|
||||
rs := []rune(string(prefix))
|
||||
// 获得子串之前的字符串的长度,便是子串在字符串的字符位置
|
||||
result = len(rs)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func ReplaceString(pattern, replace, src string) (string, error) {
|
||||
if r, err := GetRegexp(pattern); err == nil {
|
||||
return r.ReplaceAllString(src, replace), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
func Contains(haystack, needle string, startOffset ...int) int {
|
||||
length := len(haystack)
|
||||
offset := 0
|
||||
if len(startOffset) > 0 {
|
||||
offset = startOffset[0]
|
||||
}
|
||||
if length == 0 || offset > length || -offset > length {
|
||||
return -1
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
offset += length
|
||||
}
|
||||
pos := strings.Index(strings.ToLower(haystack[offset:]), strings.ToLower(needle))
|
||||
if pos == -1 {
|
||||
return -1
|
||||
}
|
||||
return pos + offset
|
||||
}
|
||||
|
||||
// 字符串模板解析
|
||||
func TemplateResolve(temp string, data any) string {
|
||||
t, _ := template.New("string-temp").Parse(temp)
|
||||
var tmplBytes bytes.Buffer
|
||||
|
||||
err := t.Execute(&tmplBytes, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmplBytes.String()
|
||||
}
|
||||
|
||||
func ReverStrTemplate(temp, str string, res map[string]any) {
|
||||
index := UnicodeIndex(temp, "{")
|
||||
ei := UnicodeIndex(temp, "}") + 1
|
||||
next := kgo.KStr.Trim(temp[ei:], " ")
|
||||
nextContain := UnicodeIndex(next, "{")
|
||||
nextIndexValue := next
|
||||
if nextContain != -1 {
|
||||
nextIndexValue = kgo.KStr.Substr(next, 0, nextContain)
|
||||
}
|
||||
key := temp[index+1 : ei-1]
|
||||
// 如果后面没有内容了,则取字符串的长度即可
|
||||
var valueLastIndex int
|
||||
if nextIndexValue == "" {
|
||||
valueLastIndex = kgo.KStr.MbStrlen(str)
|
||||
} else {
|
||||
valueLastIndex = UnicodeIndex(str, nextIndexValue)
|
||||
}
|
||||
value := kgo.KStr.Trim(kgo.KStr.Substr(str, index, valueLastIndex), " ")
|
||||
res[key] = value
|
||||
// 如果后面的还有需要解析的,则递归调用解析
|
||||
if nextContain != -1 {
|
||||
ReverStrTemplate(next, kgo.KStr.Trim(kgo.KStr.Substr(str, UnicodeIndex(str, value)+kgo.KStr.MbStrlen(value), kgo.KStr.MbStrlen(str))), res)
|
||||
}
|
||||
}
|
||||
|
||||
func B2S(bs []uint8) string {
|
||||
ba := []byte{}
|
||||
for _, b := range bs {
|
||||
ba = append(ba, byte(b))
|
||||
}
|
||||
return string(ba)
|
||||
}
|
||||
|
||||
func IdsStrToIdsIntGroup(keys string) []int64 {
|
||||
IDS := make([]int64, 0)
|
||||
ids := strings.Split(keys, ",")
|
||||
for i := 0; i < len(ids); i++ {
|
||||
ID := kgo.KConv.Str2Int(ids[i])
|
||||
IDS = append(IDS, int64(ID))
|
||||
}
|
||||
return IDS
|
||||
}
|
||||
|
||||
// 获取部门
|
||||
// isP 是父ID 否则子ID
|
||||
func DeptPCIds(deptIds []string, id int64, isP bool) []int64 {
|
||||
pRes := make([]int64, 0)
|
||||
cRes := make([]int64, 0)
|
||||
is := true
|
||||
for _, deptId := range deptIds {
|
||||
did := kgo.KConv.Str2Int64(deptId)
|
||||
if is {
|
||||
pRes = append(pRes, did)
|
||||
}
|
||||
if kgo.KConv.Str2Int64(deptId) == id {
|
||||
is = false
|
||||
}
|
||||
if !is {
|
||||
cRes = append(cRes, did)
|
||||
}
|
||||
}
|
||||
if isP {
|
||||
return pRes
|
||||
} else {
|
||||
return cRes
|
||||
}
|
||||
}
|
||||
|
||||
// 获取组织
|
||||
// isP 是父ID 否则子ID
|
||||
func OrganizationPCIds(orgIds []string, id int64, isP bool) []int64 {
|
||||
pRes := make([]int64, 0)
|
||||
cRes := make([]int64, 0)
|
||||
is := true
|
||||
for _, orgId := range orgIds {
|
||||
did := kgo.KConv.Str2Int64(orgId)
|
||||
if is {
|
||||
pRes = append(pRes, did)
|
||||
}
|
||||
if kgo.KConv.Str2Int64(orgId) == id {
|
||||
is = false
|
||||
}
|
||||
if !is {
|
||||
cRes = append(cRes, did)
|
||||
}
|
||||
}
|
||||
if isP {
|
||||
return pRes
|
||||
} else {
|
||||
return cRes
|
||||
}
|
||||
}
|
||||
24
kit/utils/str_utils_test.go
Normal file
24
kit/utils/str_utils_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIdsStrToIdsIntGroup(t *testing.T) {
|
||||
group := IdsStrToIdsIntGroup("aaa")
|
||||
t.Log(len(group))
|
||||
}
|
||||
|
||||
func TestGetRealAddressByIP(t *testing.T) {
|
||||
ip := GetRealAddressByIP("10.42.0.1")
|
||||
t.Log(ip)
|
||||
}
|
||||
|
||||
func TestDeptPCIds(t *testing.T) {
|
||||
split := strings.Split(strings.Trim("/0/2", "/"), "/")
|
||||
log.Println("split", split)
|
||||
ids := DeptPCIds(split, 2, true)
|
||||
t.Log(ids)
|
||||
}
|
||||
194
kit/utils/struct_utils_test.go
Normal file
194
kit/utils/struct_utils_test.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Src struct {
|
||||
Id *int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
CreateTime time.Time `json:"time"`
|
||||
UpdateTime time.Time
|
||||
Inner *SrcInner
|
||||
}
|
||||
|
||||
type SrcInner struct {
|
||||
Name string
|
||||
Desc string
|
||||
Id int64
|
||||
Dest *Dest
|
||||
}
|
||||
|
||||
type Dest struct {
|
||||
Username string
|
||||
Id int64
|
||||
CreateTime time.Time
|
||||
Inner *DestInner
|
||||
}
|
||||
|
||||
type DestInner struct {
|
||||
Desc string
|
||||
}
|
||||
|
||||
func TestDeepFields(t *testing.T) {
|
||||
////src := Src{Username: "test", Id: 1000, CreateTime: time.Now()}
|
||||
//si := SrcInner{Desc: "desc"}
|
||||
//src.Inner = &si
|
||||
////src.Id = 1222
|
||||
//dest := new(Dest)
|
||||
//err := structutils.Copy(dest, src)
|
||||
//if err != nil {
|
||||
// fmt.Println(err.Error())
|
||||
//} else {
|
||||
// fmt.Println(dest)
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
func TestGetFieldNames(t *testing.T) {
|
||||
//names := structutils.GetFieldNames(new(Src))
|
||||
//fmt.Println(names)
|
||||
}
|
||||
|
||||
func TestMaps2Structs(t *testing.T) {
|
||||
mapInstance := make(map[string]any)
|
||||
mapInstance["Username"] = "liang637210"
|
||||
mapInstance["Id"] = 28
|
||||
mapInstance["CreateTime"] = time.Now()
|
||||
mapInstance["Creator"] = "createor"
|
||||
mapInstance["Inner.Id"] = 10
|
||||
mapInstance["Inner.Name"] = "hahah"
|
||||
mapInstance["Inner.Desc"] = "inner desc"
|
||||
mapInstance["Inner.Dest.Username"] = "inner dest uername"
|
||||
mapInstance["Inner.Dest.Inner.Desc"] = "inner dest inner desc"
|
||||
|
||||
mapInstance2 := make(map[string]any)
|
||||
mapInstance2["Username"] = "liang6372102"
|
||||
mapInstance2["Id"] = 282
|
||||
mapInstance2["CreateTime"] = time.Now()
|
||||
mapInstance2["Creator"] = "createor2"
|
||||
mapInstance2["Inner.Id"] = 102
|
||||
mapInstance2["Inner.Name"] = "hahah2"
|
||||
mapInstance2["Inner.Desc"] = "inner desc2"
|
||||
mapInstance2["Inner.Dest.Username"] = "inner dest uername2"
|
||||
mapInstance2["Inner.Dest.Inner.Desc"] = "inner dest inner desc2"
|
||||
|
||||
maps := make([]map[string]any, 2)
|
||||
maps[0] = mapInstance
|
||||
maps[1] = mapInstance2
|
||||
res := new([]Src)
|
||||
err := Maps2Structs(maps, res)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMap2Struct(t *testing.T) {
|
||||
mapInstance := make(map[string]any)
|
||||
mapInstance["Username"] = "liang637210"
|
||||
mapInstance["Id"] = 12
|
||||
mapInstance["CreateTime"] = time.Now()
|
||||
mapInstance["Creator"] = "createor"
|
||||
mapInstance["Inner.Id"] = nil
|
||||
mapInstance["Inner.Name"] = "hahah"
|
||||
mapInstance["Inner.Desc"] = "inner desc"
|
||||
mapInstance["Inner.Dest.Username"] = "inner dest uername"
|
||||
mapInstance["Inner.Dest.Inner.Desc"] = "inner dest inner desc"
|
||||
|
||||
//innerMap := make(map[string]interface{})
|
||||
//innerMap["Name"] = "Innername"
|
||||
|
||||
//a := new(Src)
|
||||
////a.Inner = new(SrcInner)
|
||||
//
|
||||
//stime := time.Now().UnixNano()
|
||||
//for i := 0; i < 1000000; i++ {
|
||||
// err := structutils.Map2Struct(mapInstance, a)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//}
|
||||
//etime := time.Now().UnixNano()
|
||||
//fmt.Println(etime - stime)
|
||||
//if err != nil {
|
||||
// fmt.Println(err)
|
||||
//} else {
|
||||
// fmt.Println(a)
|
||||
//}
|
||||
|
||||
s := new(Src)
|
||||
//name, b := structutils.IndirectType(reflect.TypeOf(s)).FieldByName("Inner")
|
||||
//if structutils.IndirectType(name.Type).Kind() != reflect.Struct {
|
||||
// fmt.Println(name.Name + "不是结构体")
|
||||
//} else {
|
||||
// //innerType := name.Type
|
||||
// innerValue := structutils.Indirect(reflect.ValueOf(s)).FieldByName("Inner")
|
||||
// //if innerValue.IsValid() && innerValue.IsNil() {
|
||||
// // innerValue.Set(reflect.New(innerValue.Type().Elem()))
|
||||
// //}
|
||||
// if !innerValue.IsValid() {
|
||||
// fmt.Println("is valid")
|
||||
// } else {
|
||||
// //innerValue.Set(reflect.New(innerValue.Type()))
|
||||
// fmt.Println(innerValue.CanSet())
|
||||
// fmt.Println(innerValue.CanAddr())
|
||||
// //mapstructure.Decode(innerMap, innerValue.Addr().Interface())
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//fmt.Println(name, b)
|
||||
//将 map 转换为指定的结构体
|
||||
// if err := decode(mapInstance, &s); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
fmt.Printf("map2struct后得到的 struct 内容为:%v", s)
|
||||
}
|
||||
|
||||
func getPrefixKeyMap(m map[string]any) map[string]map[string]any {
|
||||
key2map := make(map[string]map[string]any)
|
||||
for k, v := range m {
|
||||
if !strings.Contains(k, ".") {
|
||||
continue
|
||||
}
|
||||
lastIndex := strings.LastIndex(k, ".")
|
||||
prefix := k[0:lastIndex]
|
||||
m2 := key2map[prefix]
|
||||
if m2 == nil {
|
||||
key2map[prefix] = map[string]any{k[lastIndex+1:]: v}
|
||||
} else {
|
||||
m2[k[lastIndex+1:]] = v
|
||||
}
|
||||
delete(m, k)
|
||||
}
|
||||
return key2map
|
||||
}
|
||||
|
||||
func TestReflect(t *testing.T) {
|
||||
type dog struct {
|
||||
LegCount int
|
||||
}
|
||||
// 获取dog实例的反射值对象
|
||||
valueOfDog := reflect.ValueOf(&dog{}).Elem()
|
||||
|
||||
// 获取legCount字段的值
|
||||
vLegCount := valueOfDog.FieldByName("LegCount")
|
||||
|
||||
fmt.Println(vLegCount.CanSet())
|
||||
fmt.Println(vLegCount.CanAddr())
|
||||
// 尝试设置legCount的值(这里会发生崩溃)
|
||||
vLegCount.SetInt(4)
|
||||
}
|
||||
|
||||
func TestTemplateResolve(t *testing.T) {
|
||||
d := make(map[string]string)
|
||||
d["Name"] = "黄先生"
|
||||
d["Age"] = "23jlfdsjf"
|
||||
resolve := TemplateResolve("{{.Name}} is name, and {{.Age}} is age", d)
|
||||
fmt.Println(resolve)
|
||||
|
||||
}
|
||||
654
kit/utils/sturct_utils.go
Normal file
654
kit/utils/sturct_utils.go
Normal file
@@ -0,0 +1,654 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Copy copy things,引用至copier
|
||||
func Copy(toValue any, fromValue any) (err error) {
|
||||
var (
|
||||
isSlice bool
|
||||
amount = 1
|
||||
from = Indirect(reflect.ValueOf(fromValue))
|
||||
to = Indirect(reflect.ValueOf(toValue))
|
||||
)
|
||||
|
||||
if !to.CanAddr() {
|
||||
return errors.New("copy to value is unaddressable")
|
||||
}
|
||||
|
||||
// Return is from value is invalid
|
||||
if !from.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
fromType := IndirectType(from.Type())
|
||||
toType := IndirectType(to.Type())
|
||||
|
||||
// Just set it if possible to assign
|
||||
// And need to do copy anyway if the type is struct
|
||||
if fromType.Kind() != reflect.Struct && from.Type().AssignableTo(to.Type()) {
|
||||
to.Set(from)
|
||||
return
|
||||
}
|
||||
|
||||
if fromType.Kind() != reflect.Struct || toType.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
if to.Kind() == reflect.Slice {
|
||||
isSlice = true
|
||||
if from.Kind() == reflect.Slice {
|
||||
amount = from.Len()
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < amount; i++ {
|
||||
var dest, source reflect.Value
|
||||
|
||||
if isSlice {
|
||||
// source
|
||||
if from.Kind() == reflect.Slice {
|
||||
source = Indirect(from.Index(i))
|
||||
} else {
|
||||
source = Indirect(from)
|
||||
}
|
||||
// dest
|
||||
dest = Indirect(reflect.New(toType).Elem())
|
||||
} else {
|
||||
source = Indirect(from)
|
||||
dest = Indirect(to)
|
||||
}
|
||||
|
||||
// check source
|
||||
if source.IsValid() {
|
||||
fromTypeFields := deepFields(fromType)
|
||||
//fmt.Printf("%#v", fromTypeFields)
|
||||
// Copy from field to field or method
|
||||
for _, field := range fromTypeFields {
|
||||
name := field.Name
|
||||
|
||||
if fromField := source.FieldByName(name); fromField.IsValid() {
|
||||
// has field
|
||||
if toField := dest.FieldByName(name); toField.IsValid() {
|
||||
if toField.CanSet() {
|
||||
if !set(toField, fromField) {
|
||||
if err := Copy(toField.Addr().Interface(), fromField.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// try to set to method
|
||||
var toMethod reflect.Value
|
||||
if dest.CanAddr() {
|
||||
toMethod = dest.Addr().MethodByName(name)
|
||||
} else {
|
||||
toMethod = dest.MethodByName(name)
|
||||
}
|
||||
|
||||
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && fromField.Type().AssignableTo(toMethod.Type().In(0)) {
|
||||
toMethod.Call([]reflect.Value{fromField})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from method to field
|
||||
for _, field := range deepFields(toType) {
|
||||
name := field.Name
|
||||
|
||||
var fromMethod reflect.Value
|
||||
if source.CanAddr() {
|
||||
fromMethod = source.Addr().MethodByName(name)
|
||||
} else {
|
||||
fromMethod = source.MethodByName(name)
|
||||
}
|
||||
|
||||
if fromMethod.IsValid() && fromMethod.Type().NumIn() == 0 && fromMethod.Type().NumOut() == 1 {
|
||||
if toField := dest.FieldByName(name); toField.IsValid() && toField.CanSet() {
|
||||
values := fromMethod.Call([]reflect.Value{})
|
||||
if len(values) >= 1 {
|
||||
set(toField, values[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if isSlice {
|
||||
if dest.Addr().Type().AssignableTo(to.Type().Elem()) {
|
||||
to.Set(reflect.Append(to, dest.Addr()))
|
||||
} else if dest.Type().AssignableTo(to.Type().Elem()) {
|
||||
to.Set(reflect.Append(to, dest))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 对结构体的每个字段以及字段值执行doWith回调函数, 包括匿名属性的字段
|
||||
func DoWithFields(str any, doWith func(fType reflect.StructField, fValue reflect.Value) error) error {
|
||||
t := IndirectType(reflect.TypeOf(str))
|
||||
if t.Kind() != reflect.Struct {
|
||||
return errors.New("非结构体")
|
||||
}
|
||||
|
||||
fieldNum := t.NumField()
|
||||
v := Indirect(reflect.ValueOf(str))
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
ft := t.Field(i)
|
||||
fv := v.Field(i)
|
||||
// 如果是匿名属性,则递归调用该方法
|
||||
if ft.Anonymous {
|
||||
DoWithFields(fv.Interface(), doWith)
|
||||
continue
|
||||
}
|
||||
err := doWith(ft, fv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deepFields(reflectType reflect.Type) []reflect.StructField {
|
||||
var fields []reflect.StructField
|
||||
|
||||
if reflectType = IndirectType(reflectType); reflectType.Kind() == reflect.Struct {
|
||||
for i := 0; i < reflectType.NumField(); i++ {
|
||||
v := reflectType.Field(i)
|
||||
if v.Anonymous {
|
||||
fields = append(fields, deepFields(v.Type)...)
|
||||
} else {
|
||||
fields = append(fields, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func Indirect(reflectValue reflect.Value) reflect.Value {
|
||||
for reflectValue.Kind() == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
return reflectValue
|
||||
}
|
||||
|
||||
func IndirectType(reflectType reflect.Type) reflect.Type {
|
||||
for reflectType.Kind() == reflect.Ptr || reflectType.Kind() == reflect.Slice {
|
||||
reflectType = reflectType.Elem()
|
||||
}
|
||||
return reflectType
|
||||
}
|
||||
|
||||
func set(to, from reflect.Value) bool {
|
||||
if from.IsValid() {
|
||||
if to.Kind() == reflect.Ptr {
|
||||
//set `to` to nil if from is nil
|
||||
if from.Kind() == reflect.Ptr && from.IsNil() {
|
||||
to.Set(reflect.Zero(to.Type()))
|
||||
return true
|
||||
} else if to.IsNil() {
|
||||
to.Set(reflect.New(to.Type().Elem()))
|
||||
}
|
||||
to = to.Elem()
|
||||
}
|
||||
|
||||
if from.Type().ConvertibleTo(to.Type()) {
|
||||
to.Set(from.Convert(to.Type()))
|
||||
} else if scanner, ok := to.Addr().Interface().(sql.Scanner); ok {
|
||||
err := scanner.Scan(from.Interface())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
} else if from.Kind() == reflect.Ptr {
|
||||
return set(to, from.Elem())
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Map2Struct(m map[string]any, s any) error {
|
||||
toValue := Indirect(reflect.ValueOf(s))
|
||||
if !toValue.CanAddr() {
|
||||
return errors.New("to value is unaddressable")
|
||||
}
|
||||
|
||||
innerStructMaps := getInnerStructMaps(m)
|
||||
if len(innerStructMaps) != 0 {
|
||||
for k, v := range innerStructMaps {
|
||||
var fieldV reflect.Value
|
||||
if strings.Contains(k, ".") {
|
||||
fieldV = getFiledValueByPath(k, toValue)
|
||||
} else {
|
||||
fieldV = toValue.FieldByName(k)
|
||||
}
|
||||
|
||||
if !fieldV.CanSet() || !fieldV.CanAddr() {
|
||||
continue
|
||||
}
|
||||
fieldT := fieldV.Type().Elem()
|
||||
if fieldT.Kind() != reflect.Struct {
|
||||
return errors.New(k + "不是结构体")
|
||||
}
|
||||
// 如果值为nil,则默认创建一个并赋值
|
||||
if fieldV.IsNil() {
|
||||
fieldV.Set(reflect.New(fieldT))
|
||||
}
|
||||
err := Map2Struct(v, fieldV.Addr().Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
var err error
|
||||
for k, v := range m {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
k = strings.Title(k)
|
||||
// 如果key含有下划线,则将其转为驼峰
|
||||
if strings.Contains(k, "_") {
|
||||
k = Case2Camel(k)
|
||||
}
|
||||
|
||||
fieldV := toValue.FieldByName(k)
|
||||
if !fieldV.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
err = decode(k, v, fieldV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Maps2Structs(maps []map[string]any, structs any) error {
|
||||
structsV := reflect.Indirect(reflect.ValueOf(structs))
|
||||
valType := structsV.Type()
|
||||
valElemType := valType.Elem()
|
||||
sliceType := reflect.SliceOf(valElemType)
|
||||
|
||||
length := len(maps)
|
||||
|
||||
valSlice := structsV
|
||||
if valSlice.IsNil() {
|
||||
// Make a new slice to hold our result, same size as the original data.
|
||||
valSlice = reflect.MakeSlice(sliceType, length, length)
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
err := Map2Struct(maps[i], valSlice.Index(i).Addr().Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
structsV.Set(valSlice)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFiledValueByPath(path string, value reflect.Value) reflect.Value {
|
||||
split := strings.Split(path, ".")
|
||||
for _, v := range split {
|
||||
if value.Type().Kind() == reflect.Ptr {
|
||||
// 如果值为nil,则创建并赋值
|
||||
if value.IsNil() {
|
||||
value.Set(reflect.New(IndirectType(value.Type())))
|
||||
}
|
||||
value = value.Elem()
|
||||
}
|
||||
value = value.FieldByName(v)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func getInnerStructMaps(m map[string]any) map[string]map[string]any {
|
||||
key2map := make(map[string]map[string]any)
|
||||
for k, v := range m {
|
||||
if !strings.Contains(k, ".") {
|
||||
continue
|
||||
}
|
||||
lastIndex := strings.LastIndex(k, ".")
|
||||
prefix := k[0:lastIndex]
|
||||
m2 := key2map[prefix]
|
||||
if m2 == nil {
|
||||
key2map[prefix] = map[string]any{k[lastIndex+1:]: v}
|
||||
} else {
|
||||
m2[k[lastIndex+1:]] = v
|
||||
}
|
||||
delete(m, k)
|
||||
}
|
||||
return key2map
|
||||
}
|
||||
|
||||
// decode等方法摘抄自mapstructure库
|
||||
|
||||
func decode(name string, input any, outVal reflect.Value) error {
|
||||
var inputVal reflect.Value
|
||||
if input != nil {
|
||||
inputVal = reflect.ValueOf(input)
|
||||
|
||||
// We need to check here if input is a typed nil. Typed nils won't
|
||||
// match the "input == nil" below so we check that here.
|
||||
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() {
|
||||
input = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !inputVal.IsValid() {
|
||||
// If the input value is invalid, then we just set the value
|
||||
// to be the zero value.
|
||||
outVal.Set(reflect.Zero(outVal.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
outputKind := getKind(outVal)
|
||||
switch outputKind {
|
||||
case reflect.Int:
|
||||
err = decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = decodeFloat(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = decodeString(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
_, err = decodePtr(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeInt(name string, data any, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetInt(dataVal.Int())
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetInt(int64(dataVal.Uint()))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetInt(int64(dataVal.Float()))
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetInt(1)
|
||||
} else {
|
||||
val.SetInt(0)
|
||||
}
|
||||
case dataKind == reflect.String:
|
||||
i, err := strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetInt(i)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as int: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Int64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
val.SetInt(i)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUint(name string, data any, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
i := dataVal.Int()
|
||||
if i < 0 {
|
||||
return fmt.Errorf("cannot parse '%s', %d overflows uint",
|
||||
name, i)
|
||||
}
|
||||
val.SetUint(uint64(i))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetUint(dataVal.Uint())
|
||||
case dataKind == reflect.Float32:
|
||||
f := dataVal.Float()
|
||||
if f < 0 {
|
||||
return fmt.Errorf("cannot parse '%s', %f overflows uint",
|
||||
name, f)
|
||||
}
|
||||
val.SetUint(uint64(f))
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetUint(1)
|
||||
} else {
|
||||
val.SetUint(0)
|
||||
}
|
||||
case dataKind == reflect.String:
|
||||
i, err := strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetUint(i)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Int64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
if i < 0 {
|
||||
return fmt.Errorf("cannot parse '%s', %d overflows uint",
|
||||
name, i)
|
||||
}
|
||||
val.SetUint(uint64(i))
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeFloat(name string, data any, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
dataType := dataVal.Type()
|
||||
|
||||
switch {
|
||||
case dataKind == reflect.Int:
|
||||
val.SetFloat(float64(dataVal.Int()))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetFloat(float64(dataVal.Uint()))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetFloat(dataVal.Float())
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetFloat(1)
|
||||
} else {
|
||||
val.SetFloat(0)
|
||||
}
|
||||
case dataKind == reflect.String:
|
||||
f, err := strconv.ParseFloat(dataVal.String(), val.Type().Bits())
|
||||
if err == nil {
|
||||
val.SetFloat(f)
|
||||
} else {
|
||||
return fmt.Errorf("cannot parse '%s' as float: %s", name, err)
|
||||
}
|
||||
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
|
||||
jn := data.(json.Number)
|
||||
i, err := jn.Float64()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"error decoding json.Number into %s: %s", name, err)
|
||||
}
|
||||
val.SetFloat(i)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeString(name string, data any, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataKind := getKind(dataVal)
|
||||
|
||||
converted := true
|
||||
switch {
|
||||
case dataKind == reflect.String:
|
||||
val.SetString(dataVal.String())
|
||||
case dataKind == reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
val.SetString("1")
|
||||
} else {
|
||||
val.SetString("0")
|
||||
}
|
||||
case dataKind == reflect.Int:
|
||||
val.SetString(strconv.FormatInt(dataVal.Int(), 10))
|
||||
case dataKind == reflect.Uint:
|
||||
val.SetString(strconv.FormatUint(dataVal.Uint(), 10))
|
||||
case dataKind == reflect.Float32:
|
||||
val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64))
|
||||
case dataKind == reflect.Slice,
|
||||
dataKind == reflect.Array:
|
||||
dataType := dataVal.Type()
|
||||
elemKind := dataType.Elem().Kind()
|
||||
switch elemKind {
|
||||
case reflect.Uint8:
|
||||
var uints []uint8
|
||||
if dataKind == reflect.Array {
|
||||
uints = make([]uint8, dataVal.Len(), dataVal.Len())
|
||||
for i := range uints {
|
||||
uints[i] = dataVal.Index(i).Interface().(uint8)
|
||||
}
|
||||
} else {
|
||||
uints = dataVal.Interface().([]uint8)
|
||||
}
|
||||
val.SetString(string(uints))
|
||||
default:
|
||||
converted = false
|
||||
}
|
||||
default:
|
||||
converted = false
|
||||
}
|
||||
|
||||
if !converted {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodePtr(name string, data any, val reflect.Value) (bool, error) {
|
||||
// If the input data is nil, then we want to just set the output
|
||||
// pointer to be nil as well.
|
||||
isNil := data == nil
|
||||
if !isNil {
|
||||
switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {
|
||||
case reflect.Chan,
|
||||
reflect.Func,
|
||||
reflect.Interface,
|
||||
reflect.Map,
|
||||
reflect.Ptr,
|
||||
reflect.Slice:
|
||||
isNil = v.IsNil()
|
||||
}
|
||||
}
|
||||
if isNil {
|
||||
if !val.IsNil() && val.CanSet() {
|
||||
nilValue := reflect.New(val.Type()).Elem()
|
||||
val.Set(nilValue)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Create an element of the concrete (non pointer) type and decode
|
||||
// into that. Then set the value of the pointer to this type.
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
if val.CanSet() {
|
||||
realVal := val
|
||||
if realVal.IsNil() {
|
||||
realVal = reflect.New(valElemType)
|
||||
}
|
||||
|
||||
if err := decode(name, data, reflect.Indirect(realVal)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
val.Set(realVal)
|
||||
} else {
|
||||
if err := decode(name, data, reflect.Indirect(val)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func getKind(val reflect.Value) reflect.Kind {
|
||||
kind := val.Kind()
|
||||
|
||||
switch {
|
||||
case kind >= reflect.Int && kind <= reflect.Int64:
|
||||
return reflect.Int
|
||||
case kind >= reflect.Uint && kind <= reflect.Uint64:
|
||||
return reflect.Uint
|
||||
case kind >= reflect.Float32 && kind <= reflect.Float64:
|
||||
return reflect.Float32
|
||||
default:
|
||||
return kind
|
||||
}
|
||||
}
|
||||
|
||||
// 下划线写法转为驼峰写法
|
||||
func Case2Camel(name string) string {
|
||||
name = strings.Replace(name, "_", " ", -1)
|
||||
name = strings.Title(name)
|
||||
return strings.Replace(name, " ", "", -1)
|
||||
}
|
||||
|
||||
func IsBlank(value reflect.Value) bool {
|
||||
switch value.Kind() {
|
||||
case reflect.String:
|
||||
return value.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !value.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return value.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return value.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return value.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return value.IsNil()
|
||||
}
|
||||
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
|
||||
}
|
||||
28
kit/utils/template.go
Normal file
28
kit/utils/template.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func parse(t *template.Template, vars any) string {
|
||||
var tmplBytes bytes.Buffer
|
||||
|
||||
err := t.Execute(&tmplBytes, vars)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tmplBytes.String()
|
||||
}
|
||||
|
||||
// 模板字符串解析
|
||||
// str 模板字符串
|
||||
// vars 参数变量
|
||||
func TemplateParse(str string, vars any) string {
|
||||
tmpl, err := template.New("tmpl").Parse(str)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parse(tmpl, vars)
|
||||
}
|
||||
74
kit/utils/tree_utils.go
Normal file
74
kit/utils/tree_utils.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package utils
|
||||
|
||||
// ConvertToINodeArray 其他的结构体想要生成菜单树,直接实现这个接口
|
||||
type INode interface {
|
||||
// GetId获取id
|
||||
GetId() int
|
||||
// GetPid 获取父id
|
||||
GetPid() int
|
||||
// IsRoot 判断当前节点是否是顶层根节点
|
||||
IsRoot() bool
|
||||
|
||||
SetChildren(childern any)
|
||||
}
|
||||
|
||||
type INodes []INode
|
||||
|
||||
func (nodes INodes) Len() int {
|
||||
return len(nodes)
|
||||
}
|
||||
func (nodes INodes) Swap(i, j int) {
|
||||
nodes[i], nodes[j] = nodes[j], nodes[i]
|
||||
}
|
||||
func (nodes INodes) Less(i, j int) bool {
|
||||
return nodes[i].GetId() < nodes[j].GetId()
|
||||
}
|
||||
|
||||
// GenerateTree 自定义的结构体实现 INode 接口后调用此方法生成树结构
|
||||
// nodes 需要生成树的节点
|
||||
// selectedNode 生成树后选中的节点
|
||||
// menuTrees 生成成功后的树结构对象
|
||||
func GenerateTree(nodes []INode) (trees []INode) {
|
||||
trees = []INode{}
|
||||
// 定义顶层根和子节点
|
||||
var roots, childs []INode
|
||||
for _, v := range nodes {
|
||||
if v.IsRoot() {
|
||||
// 判断顶层根节点
|
||||
roots = append(roots, v)
|
||||
}
|
||||
childs = append(childs, v)
|
||||
}
|
||||
|
||||
for _, v := range roots {
|
||||
// 递归
|
||||
setChildren(v, childs)
|
||||
trees = append(trees, v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// recursiveTree 递归生成树结构
|
||||
// tree 递归的树对象
|
||||
// nodes 递归的节点
|
||||
// selectedNodes 选中的节点
|
||||
func setChildren(parent INode, nodes []INode) {
|
||||
children := []INode{}
|
||||
for _, v := range nodes {
|
||||
if v.IsRoot() {
|
||||
// 如果当前节点是顶层根节点就跳过
|
||||
continue
|
||||
}
|
||||
if parent.GetId() == v.GetPid() {
|
||||
children = append(children, v)
|
||||
}
|
||||
}
|
||||
if len(children) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
parent.SetChildren(children)
|
||||
for _, c := range children {
|
||||
setChildren(c, nodes)
|
||||
}
|
||||
}
|
||||
27
kit/utils/yml.go
Normal file
27
kit/utils/yml.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 从指定路径加载yaml文件
|
||||
func LoadYml(path string, out any) error {
|
||||
yamlFileBytes, readErr := ioutil.ReadFile(path)
|
||||
if readErr != nil {
|
||||
return readErr
|
||||
}
|
||||
// yaml解析
|
||||
err := yaml.Unmarshal(yamlFileBytes, out)
|
||||
if err != nil {
|
||||
return errors.New("无法解析 [" + path + "] -- " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadYmlByString(yamlStr string, out any) error {
|
||||
// yaml解析
|
||||
return yaml.Unmarshal([]byte(yamlStr), out)
|
||||
}
|
||||
Reference in New Issue
Block a user