diff --git a/app/constants/common/common.go b/app/constants/common/common.go
index a3a296b..e731efb 100644
--- a/app/constants/common/common.go
+++ b/app/constants/common/common.go
@@ -1,7 +1,7 @@
 package common
 
 const (
-	TOKEN_PRE   = "player_token_"
-	TOKEN_Admin = "Admin_token_"
-	ADMIN_V1    = "/admin/api/v1"
+	TOKEN_PRE      = "player_token_"
+	TOKEN_Admin    = "Admin_token_"
+	ADMIN_OAUTH_V1 = "/admin/api/oauth/v1"
 )
diff --git a/app/http/controllers/backend/db_controller.go b/app/http/controllers/backend/db_controller.go
new file mode 100644
index 0000000..2c902c2
--- /dev/null
+++ b/app/http/controllers/backend/db_controller.go
@@ -0,0 +1,27 @@
+package backend
+
+import (
+	"cron_admin/app/constants/errorcode"
+	"cron_admin/app/http/controllers"
+	"cron_admin/app/http/entities/backend"
+	"cron_admin/app/services/db_service"
+	"cron_admin/app/utils/helper"
+	"cron_admin/app/utils/mapstructure"
+	"github.com/gin-gonic/gin"
+)
+
+func DbList(c *gin.Context) {
+	request := controllers.GetRequest(c).(*backend.DbListRequest)
+	count, DbListInfo, err := db_service.DbList(request, request.Page, request.Limit)
+	if err != nil {
+		controllers.HandCodeRes(c, nil, errorcode.ParamError)
+	} else {
+		var DbListResponse []backend.DbListResponse
+		_ = mapstructure.DecodeWithTime(DbListInfo, &DbListResponse, helper.DefaultFormatLayout)
+		controllers.HandRes(c, gin.H{"data": DbListResponse, "count": count}, err)
+	}
+}
+
+func DbAdd(c *gin.Context) {
+	request := controllers.GetRequest(c).(*backend.DbAddRequest)
+}
diff --git a/app/http/entities/backend/db.go b/app/http/entities/backend/db.go
new file mode 100644
index 0000000..6d49c98
--- /dev/null
+++ b/app/http/entities/backend/db.go
@@ -0,0 +1,23 @@
+package backend
+
+type DbListRequest struct {
+	Page   int    `json:"page" validate:"required" form:"page" example:"1"`
+	Limit  int    `json:"limit" validate:"required" form:"limit" example:"10"`
+	DbName string `json:"db_name" form:"db_name" example:""`
+	Status int    `json:"status" form:"status" example:"1"`
+	DbType string `json:"db_type" form:"db_type" example:"mysql"`
+}
+
+type DbListResponse struct {
+	DbId         string `json:"db_id"`
+	DbName       string `json:"db_name"`
+	DbType       string `json:"db_type"`
+	Status       int    `json:"status"`
+	DbPermission int    `json:"db_permission"`
+	Source       string `json:"source"`
+	Desc         string `json:"desc"`
+	CreateTime   string `json:"create_time"`
+}
+
+type DbAddRequest struct {
+}
diff --git a/app/http/requestmapping/backend.go b/app/http/requestmapping/backend.go
index 6d05180..332083e 100644
--- a/app/http/requestmapping/backend.go
+++ b/app/http/requestmapping/backend.go
@@ -1,8 +1,13 @@
 package requestmapping
 
+import (
+	"cron_admin/app/constants/common"
+	"cron_admin/app/http/entities/backend"
+)
+
 var BackendRequestMap = map[string]func() interface{}{
 
-	//common.ADMIN_V1 + "/product/create": func() interface{} {
-	//	return new(backend.ProductCreateRequest)
-	//},
+	common.ADMIN_OAUTH_V1 + "/sql/list": func() interface{} {
+		return new(backend.DbListRequest)
+	},
 }
diff --git a/app/http/routes/admin.go b/app/http/routes/admin.go
index 8b02160..3a8e16a 100644
--- a/app/http/routes/admin.go
+++ b/app/http/routes/admin.go
@@ -38,7 +38,7 @@ func RegisterAdminRoute(router *gin.Engine) {
 				//数据库管理
 				sql := v1.Group("/sql")
 				{
-					sql.GET("/list", backend.Empty)
+					sql.POST("/list", backend.DbList)
 				}
 				//任务
 				cmd := v1.Group("/cmd")
diff --git a/app/services/db_service/db_service.go b/app/services/db_service/db_service.go
new file mode 100644
index 0000000..e9e46d0
--- /dev/null
+++ b/app/services/db_service/db_service.go
@@ -0,0 +1,30 @@
+package db_service
+
+import (
+	"cron_admin/app/http/entities/backend"
+	"cron_admin/app/models/crondbmodel"
+	"xorm.io/builder"
+)
+
+func DbList(request *backend.DbListRequest, page int, limit int) (count int64, DbListInfo []crondbmodel.CronDb, err error) {
+	conn := builder.NewCond()
+	if request.DbName != "" {
+		conn = conn.And(builder.Like{"DbName", request.DbName})
+	}
+	if request.DbType != "" {
+		conn = conn.And(builder.Like{"DbType", request.DbType})
+	}
+	if request.Status != 0 {
+		conn = conn.And(builder.Eq{"Status": request.Status})
+	}
+	session := crondbmodel.GetInstance().GetDb().Where(conn)
+	if page != 0 && limit != 0 {
+		session = session.Limit(limit, (page-1)*limit)
+	}
+	count, err = session.FindAndCount(&DbListInfo)
+	if err != nil {
+		return
+	}
+	return
+
+}
diff --git a/app/utils/helper/convert.go b/app/utils/helper/convert.go
new file mode 100644
index 0000000..3fe4303
--- /dev/null
+++ b/app/utils/helper/convert.go
@@ -0,0 +1,19 @@
+package helper
+
+import "github.com/duke-git/lancet/v2/slice"
+
+func SliceConvertSlice[O int | int32 | int64 | int8, T int | int32 | int64 | int8](inputSlice []T) []O {
+	output := slice.Map(inputSlice, func(_ int, item T) O {
+		return O(item)
+	})
+	return output
+}
+
+// MergeInt64Slices 合并两个 []O 切片为一个新的 []O 切片
+func MergeInt64Slices[O int | int32 | int64 | int8](a, b []O) []O {
+	length := len(a) + len(b)
+	merged := make([]O, length)
+	copy(merged, a)
+	copy(merged[len(a):], b)
+	return merged
+}
diff --git a/app/utils/helper/convert_test.go b/app/utils/helper/convert_test.go
new file mode 100644
index 0000000..e896842
--- /dev/null
+++ b/app/utils/helper/convert_test.go
@@ -0,0 +1,15 @@
+package helper
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestMergeInt64Slices(t *testing.T) {
+	v1 := []int64{1, 2}
+	v2 := []int64{4, 3}
+	v := MergeInt64Slices(v1, v2)
+	fmt.Printf("%v", v)
+	vv := append(v1, v2...)
+	fmt.Printf("%v", vv)
+}
diff --git a/app/utils/helper/datetime.go b/app/utils/helper/datetime.go
new file mode 100755
index 0000000..1b7ac14
--- /dev/null
+++ b/app/utils/helper/datetime.go
@@ -0,0 +1,203 @@
+package helper
+
+import (
+	"time"
+
+	"github.com/pkg/errors"
+
+	"google.golang.org/protobuf/types/known/timestamppb"
+)
+
+const (
+	DefaultParseFormatLayout = "2006-1-02 15:04:05"         // 默认解析时间格式
+	DefaultFormatLayout      = "2006-01-02 15:04:05"        // 默认时间格式
+	MicroFormatLayout        = "2006-01-02 15:04:05.000000" // 微秒时间格式
+	FormatLayout             = "20060102150405"             // 导出时间格式
+)
+
+func AfterMinutesUnix(minutes int64) int64 {
+	// 获取当前时间
+	currentTime := time.Now()
+	// 将当前时间增加5分钟
+	currentTime = currentTime.Add(time.Duration(minutes) * time.Minute)
+	// 转换为Unix时间戳(秒)
+	return currentTime.Unix()
+}
+
+func DateStringToTime(localTimeStr string) (time.Time, error) {
+	defer func() {
+		if err := recover(); err != nil {
+			LogErr(errors.New("时间转换错误:" + localTimeStr))
+		}
+	}()
+	if localTimeStr == "" {
+		return time.Time{}, nil
+	}
+
+	layouts := []string{
+		DefaultParseFormatLayout,
+		DefaultFormatLayout,
+		"2006-01-02 15:04",
+		"2006-1-02 15:04",
+		"2006-01-02",
+		"2006-1-02",
+		time.RFC3339Nano,
+	}
+
+	var (
+		localTime time.Time
+		err       error
+	)
+
+	loc, _ := time.LoadLocation(GetTimeZone())
+	for _, layout := range layouts {
+		localTime, err = time.ParseInLocation(layout, localTimeStr, loc)
+		if err == nil {
+			break
+		}
+	}
+
+	if err != nil {
+		return time.Time{}, errors.Errorf("解析时间错误: %s", err.Error())
+	}
+
+	return localTime, nil
+}
+
+// TimeFormat 时间戳转时间格式
+func TimeFormat(inTime *time.Time, layout ...string) string {
+	if inTime == nil {
+		return ""
+	}
+	if len(layout) == 0 {
+		layout = []string{DefaultFormatLayout}
+	}
+	return inTime.Format(layout[0])
+}
+
+// TimeStampFormat 时间戳转时间格式
+func TimeStampFormat(timestamp int64, layout ...string) string {
+	if timestamp == 0 {
+		return ""
+	}
+	if len(layout) == 0 {
+		layout = []string{DefaultFormatLayout}
+	}
+	return time.Unix(timestamp, 0).Format(layout[0])
+}
+
+// Int32TimeStampFormat int32类型时间戳转时间格式
+func Int32TimeStampFormat(timestamp int32, layout ...string) string {
+	return TimeStampFormat(int64(timestamp), layout...)
+}
+
+// IsoDateStringToTimeStamp iso格式字符串转时间戳
+func IsoDateStringToTimeStamp(isoTimeStr string) (int64, error) {
+	isoTime, err := time.ParseInLocation(time.RFC3339Nano, isoTimeStr, time.Local)
+	if err != nil {
+		return 0, err
+	}
+	return isoTime.Unix(), nil
+}
+
+// LayOutDateStringToTimeStamp layout格式字符串转时间戳
+func LayOutDateStringToTimeStamp(localTimeStr string) (int64, error) {
+	localTime, err := time.ParseInLocation(DefaultFormatLayout, localTimeStr, time.Local)
+	if err != nil {
+		return 0, err
+	}
+	return localTime.Unix(), nil
+}
+
+// DateStringToTimeStamp 字符串转时间戳(兼容格式)
+func DateStringToTimeStamp(date string) (int64, error) {
+	u, err := IsoDateStringToTimeStamp(date)
+	if err != nil {
+		u, err = LayOutDateStringToTimeStamp(date)
+	}
+	return u, err
+}
+
+// DateStringToTimeStampInt32 字符串转时间戳(兼容格式)
+func DateStringToTimeStampInt32(date string) (int32, error) {
+	u, err := DateStringToTimeStamp(date)
+	return int32(u), err
+}
+
+// BatchDateStringToTimeStampInt32 批量字符串转时间戳
+func BatchDateStringToTimeStampInt32(dates []string) ([]int32, error) {
+	res := make([]int32, 0, len(dates))
+	for _, date := range dates {
+		u, err := DateStringToTimeStampInt32(date)
+		if err != nil {
+			return nil, err
+		}
+		res = append(res, u)
+	}
+	return res, nil
+}
+
+// GoogleTimeToString google proto时间转字符串
+func GoogleTimeToString(date *timestamppb.Timestamp) string {
+	loc, _ := time.LoadLocation(GetTimeZone())
+	return date.AsTime().In(loc).Format(DefaultFormatLayout)
+}
+
+// GoogleTimeToMicroString google proto时间转字符串
+func GoogleTimeToMicroString(date *timestamppb.Timestamp) string {
+	loc, _ := time.LoadLocation(GetTimeZone())
+	return date.AsTime().In(loc).Format(MicroFormatLayout)
+}
+
+func DateStringToTimeStampV2(localTimeStr string) (int64, error) {
+	if localTimeStr == "" {
+		return 0, nil
+	}
+
+	layouts := []string{
+		DefaultParseFormatLayout,
+		DefaultFormatLayout,
+		"2006-01-02 15:04",
+		"2006-1-02 15:04",
+		"2006-01-02",
+		"2006-1-02",
+		time.RFC3339Nano,
+	}
+
+	var (
+		localTime time.Time
+		err       error
+	)
+
+	loc, _ := time.LoadLocation(GetTimeZone())
+	for _, layout := range layouts {
+		localTime, err = time.ParseInLocation(layout, localTimeStr, loc)
+		if err == nil {
+			break
+		}
+	}
+
+	if err != nil {
+		return 0, errors.Errorf("解析时间错误: %s", err.Error())
+	}
+
+	return localTime.Unix(), nil
+}
+
+// IsTimeStrBefore 时间比较 格式:2006-01-02 15:04:05
+func IsTimeStrBefore(beforeTime, afterTime string) bool {
+	time1, _ := time.Parse(DefaultFormatLayout, beforeTime)
+	time2, _ := time.Parse(DefaultFormatLayout, afterTime)
+	if time1.Before(time2) {
+		return true
+	}
+	return false
+}
+
+// IsTimeBefore 时间比较 格式:2006-01-02 15:04:05
+func IsTimeBefore(beforeTime, afterTime time.Time) bool {
+	if beforeTime.Before(afterTime) {
+		return true
+	}
+	return false
+}
diff --git a/app/utils/helper/datetime_test.go b/app/utils/helper/datetime_test.go
new file mode 100644
index 0000000..350e2bf
--- /dev/null
+++ b/app/utils/helper/datetime_test.go
@@ -0,0 +1,14 @@
+package helper
+
+import (
+	"testing"
+)
+
+func TestDateStringToTime(t *testing.T) {
+	got, err := DateStringToTime("2024-01-01 00:00:00")
+	if err != nil {
+		t.Errorf("DateStringToTime() error = %v", err)
+		return
+	}
+	t.Log(got)
+}
diff --git a/app/utils/helper/env.go b/app/utils/helper/env.go
new file mode 100755
index 0000000..17e4c10
--- /dev/null
+++ b/app/utils/helper/env.go
@@ -0,0 +1,23 @@
+package helper
+
+import "os"
+
+const (
+	DefaultTimeZone = "Asia/Shanghai" // 时区
+)
+
+func GetEnv(name string) string {
+	return os.Getenv(name)
+}
+
+func GetEnvWithDefault(name string, defaultValue string) string {
+	value := GetEnv(name)
+	if value == "" {
+		return defaultValue
+	}
+	return value
+}
+
+func GetTimeZone() string {
+	return GetEnvWithDefault("TZ", DefaultTimeZone)
+}
diff --git a/app/utils/helper/import.go b/app/utils/helper/import.go
new file mode 100644
index 0000000..dff64d1
--- /dev/null
+++ b/app/utils/helper/import.go
@@ -0,0 +1,69 @@
+package helper
+
+import (
+	"fmt"
+	"github.com/go-kratos/kratos/v2/transport/http"
+	"github.com/pkg/errors"
+	"github.com/xuri/excelize/v2"
+	"strconv"
+)
+
+func ImportFile(ctx http.Context) (map[string][]int32, error) {
+	// 获取上传的文件
+	file, _, err := ctx.Request().FormFile("file")
+	if err != nil {
+		return nil, fmt.Errorf("failed to get form file: %w", err)
+	}
+	defer file.Close()
+
+	// 解析Excel文件
+	f, err := excelize.OpenReader(file)
+	if err != nil {
+		return nil, fmt.Errorf("failed to open excel file: %w", err)
+	}
+	defer f.Close()
+
+	// 获取第一个工作表的名称
+	sheetNames := f.GetSheetList()
+
+	if len(sheetNames) == 0 {
+		return nil, fmt.Errorf("无效的excel,未获取到对应的sheet")
+	}
+
+	// 获取所有行
+	rows, err := f.GetRows(sheetNames[0])
+	if err != nil {
+		return nil, fmt.Errorf("failed to get rows from Excel sheet: %w", err)
+	}
+	if len(rows) == 0 {
+		return nil, fmt.Errorf("无效的excel,未获取到对应的记录数据")
+	}
+
+	actualHeaders := rows[0] // 获取第一行作为表头
+	var key int
+	for i, v := range actualHeaders {
+		if v == "ID" {
+			key = i
+		}
+	}
+
+	// 定义一个map来存储订单ID到SendInfo的映射
+	var ids []int32
+	// 遍历行并处理数据
+	for i, row := range rows {
+
+		if i == 0 {
+			continue // 跳过头栏
+		}
+		if row[key] != "" {
+			id, _ := strconv.Atoi(row[key])
+			ids = append(ids, int32(id))
+		}
+	}
+	if len(ids) == 0 {
+		return nil, errors.New("未获取到数据")
+	}
+	idsPb := map[string][]int32{"ids": ids}
+
+	return idsPb, nil
+}
diff --git a/app/utils/helper/ip.go b/app/utils/helper/ip.go
new file mode 100644
index 0000000..5e2285f
--- /dev/null
+++ b/app/utils/helper/ip.go
@@ -0,0 +1,46 @@
+package helper
+
+import (
+	"context"
+	"github.com/go-kratos/kratos/v2/transport/http"
+	"net"
+	"strings"
+)
+
+// GetHeaderRealIP 获取header头中的真实IP
+func GetHeaderRealIP(ctx context.Context) string {
+	serverContext, isOk := http.RequestFromServerContext(ctx)
+	if isOk {
+		return serverContext.Header.Get("X-Real-Ip")
+	}
+	return ""
+}
+
+// GetClientIP 获取客户端IP
+func GetClientIP(r *http.Request) string {
+	xForwardedFor := r.Header.Get("X-Forwarded-For")
+	ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0])
+	if ip != "" {
+		return ip
+	}
+
+	ip = strings.TrimSpace(r.Header.Get("X-Real-Ip"))
+	if ip != "" {
+		return ip
+	}
+
+	if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil {
+		return ip
+	}
+
+	return ""
+}
+
+// IPString2Long 把ip字符串转为数值(ipv4)
+func IPString2Long(ip string) uint {
+	b := net.ParseIP(ip).To4()
+	if b == nil {
+		return 0
+	}
+	return uint(b[3]) | uint(b[2])<<8 | uint(b[1])<<16 | uint(b[0])<<24
+}
diff --git a/app/utils/helper/snowflake.go b/app/utils/helper/snowflake.go
new file mode 100644
index 0000000..85d270e
--- /dev/null
+++ b/app/utils/helper/snowflake.go
@@ -0,0 +1,16 @@
+package helper
+
+import (
+	"strconv"
+	"time"
+
+	"github.com/bwmarrin/snowflake"
+)
+
+func GenerateIDString() (string, error) {
+	node, err := snowflake.NewNode(time.Now().UnixMilli() % 1023)
+	if err != nil {
+		return "", err
+	}
+	return strconv.FormatInt(node.Generate().Int64(), 36), nil
+}
diff --git a/app/utils/helper/snowflake_test.go b/app/utils/helper/snowflake_test.go
new file mode 100644
index 0000000..4b60665
--- /dev/null
+++ b/app/utils/helper/snowflake_test.go
@@ -0,0 +1,14 @@
+package helper
+
+import (
+	"testing"
+)
+
+func TestGenerateIDString(t *testing.T) {
+	s, err := GenerateIDString()
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	t.Log(s)
+}
diff --git a/app/utils/helper/string.go b/app/utils/helper/string.go
new file mode 100755
index 0000000..a8eb0f2
--- /dev/null
+++ b/app/utils/helper/string.go
@@ -0,0 +1,94 @@
+package helper
+
+import (
+	"math/rand"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+	"unicode"
+)
+
+func StringToInt64(s string) int64 {
+	i, _ := strconv.ParseInt(s, 10, 64)
+	return i
+}
+
+func StringToInt32(s string) int32 {
+	i, _ := strconv.ParseInt(s, 10, 64)
+	return int32(i)
+}
+
+// StringToInt32WithError 将字符串转换为int32
+func StringToInt32WithError(s string) (int32, error) {
+	i, err := strconv.ParseInt(s, 10, 64)
+	if err != nil {
+		return 0, err
+	}
+	return int32(i), nil
+}
+
+// StringToAsterisk 替换密钥中间字符为 *
+func StringToAsterisk(value string) string {
+	length := len(value)
+	if length == 0 {
+		return ""
+	}
+	asterisk := "*"
+	if length < 4 {
+		asterisk = strings.Repeat(asterisk, length)
+		return strings.Replace(value, value, asterisk, 1)
+	}
+
+	offset := length / 4
+	asterisk = strings.Repeat(asterisk, length-offset*2)
+	return value[:offset] + asterisk + value[length-offset:]
+}
+
+func StrSuffixNumSort(in []string) []string {
+	sort.Slice(in, func(i, j int) bool {
+		return getSuffixNumber(in[i]) < getSuffixNumber(in[j])
+	})
+	var result []string
+	for _, str := range in {
+		result = append(result, str)
+	}
+
+	return result
+}
+
+// 获取字符串中最后面的后缀数字
+func getSuffixNumber(s string) int {
+	parts := strings.Split(s, "_")
+	lastPart := parts[len(parts)-1] // 获取最后一个部分
+	numStr := ""
+	for i := len(lastPart) - 1; i >= 0; i-- {
+		if _, err := strconv.Atoi(string(lastPart[i])); err == nil {
+			numStr = string(lastPart[i]) + numStr
+		} else {
+			break
+		}
+	}
+	num, _ := strconv.Atoi(numStr)
+	return num
+}
+
+func GetRandomStr(length int) string {
+	rand.Seed(time.Now().UnixNano())
+	// 去掉容易搞错的字符,例如0,1,l,L,0,O,o,I,i
+	const charset = "abcdefghjklmnpqrstuvwxyzABCDEFGHIJKMNPQRSTUVWXYZ123456789"
+	password := make([]byte, length)
+	for i := range password {
+		password[i] = charset[rand.Intn(len(charset))]
+	}
+	return string(password)
+}
+
+func ToTitleFirst(s string) string {
+	if s == "" {
+		return s
+	}
+	runes := []rune(s)
+	runes[0] = unicode.ToUpper(runes[0])
+	return string(runes)
+}
diff --git a/app/utils/helper/string_test.go b/app/utils/helper/string_test.go
new file mode 100755
index 0000000..a018f9c
--- /dev/null
+++ b/app/utils/helper/string_test.go
@@ -0,0 +1,58 @@
+package helper
+
+import (
+	"fmt"
+	"testing"
+)
+
+// 测试 StringToInt32方法
+func TestStringToInt32(t *testing.T) {
+	// 测试用例
+	type args struct {
+		s string
+	}
+	tests := []struct {
+		name string
+		args args
+		want int32
+	}{
+		{name: "测试1", args: args{s: "123"}, want: 123},
+		{name: "测试2", args: args{s: "123.123"}, want: 0},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			// 调用方法
+			if got := StringToInt32(tt.args.s); got != tt.want {
+				// 判断结果
+				t.Errorf("StringToInt32() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestReplaceStar(t *testing.T) {
+	type args struct {
+		secretKey string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{name: "测试1", args: args{secretKey: "123456789"}, want: "12******89"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := StringToAsterisk(tt.args.secretKey)
+			t.Logf("StringToAsterisk() = %v, want %v \n", got, tt.want)
+			if got != tt.want {
+				t.Errorf("StringToAsterisk() = %v, want %v \n", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestRandomPassword(t *testing.T) {
+	password := GetRandomStr(6)
+	fmt.Println("生成的随机密码为:", password)
+}
diff --git a/app/utils/helper/util.go b/app/utils/helper/util.go
new file mode 100644
index 0000000..6c9f803
--- /dev/null
+++ b/app/utils/helper/util.go
@@ -0,0 +1,118 @@
+package helper
+
+import (
+	"crypto/md5"
+	"fmt"
+	"github.com/golang/protobuf/proto"
+	"net/url"
+	"os"
+	"reflect"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func Log(name string, msg ...interface{}) {
+	_, file, line, _ := runtime.Caller(1)
+	timeLayout := "2006-01-01 03:04:05" //转化所需模板
+	var datetime = time.Unix(time.Now().Unix(), 0).Format(timeLayout)
+	fmt.Println(name, msg, file, line, datetime)
+}
+
+func LogErr(err error) {
+	if err != nil {
+		Log("err", err.Error())
+	}
+}
+
+func Path(path string) (string, error) {
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		if err := os.MkdirAll(path, 0777); err != nil {
+			return "", err
+		}
+	}
+	return path, nil
+}
+
+func FileExists(filePath string) bool {
+	_, err := os.Stat(filePath)
+	return err == nil || os.IsExist(err)
+}
+
+// TrackTime 运行时间  defer TrackTime()()
+func TrackTime() func() {
+	pre := time.Now()
+	return func() {
+		elapsed := time.Since(pre)
+		fmt.Println("elapsed:", elapsed)
+	}
+}
+
+// Ter 三目运算  Ter(true, 1, 2)
+func Ter[T any](cond bool, a, b T) T {
+	if cond {
+		return a
+	}
+	return b
+}
+
+// get参转结构体
+func ParseQueryStringToStruct(query string, target interface{}) error {
+	// 解析查询字符串
+	values, err := url.ParseQuery(query)
+	if err != nil {
+		return err
+	}
+
+	// 获取目标结构体的反射值
+	rv := reflect.ValueOf(target).Elem()
+
+	// 遍历查询字符串中的每个键值对
+	for key, values := range values {
+		// 假设每个查询参数只传递一个值(如果有多个值,这里只取第一个)
+		value := values[0]
+
+		// 查找目标结构体中对应的字段
+		field := rv.FieldByName(strings.Title(key)) // 假设字段名与查询参数名相同,但首字母大写
+		if !field.IsValid() || !field.CanSet() {
+			// 如果找不到对应的字段,或者字段不可设置,则跳过
+			continue
+		}
+
+		// 根据字段类型设置值
+		switch field.Kind() {
+		case reflect.String:
+			field.SetString(value)
+		case reflect.Int:
+			intValue, err := strconv.Atoi(value)
+			if err != nil {
+				return err
+			}
+			field.SetInt(int64(intValue))
+		// 可以根据需要添加更多类型处理
+		default:
+			// 不支持的类型,可以选择跳过或返回错误
+			continue
+		}
+	}
+
+	return nil
+}
+
+// GenerateMD5Hash 生成 MD5 的函数,接收一个实现了 proto.Message 接口的结构体
+func GenerateMD5Hash(msg proto.Message) (string, error) {
+	// 使用 proto.Marshal 序列化结构体,proto.Marshal 接受任何实现 proto.Message 接口的结构体
+	data, err := proto.Marshal(msg)
+	if err != nil {
+		return "", fmt.Errorf("failed to serialize message: %v", err)
+	}
+
+	// 计算 MD5
+	hash := md5.New()
+	hash.Write(data)
+	hashBytes := hash.Sum(nil)
+
+	// 返回 MD5 值的十六进制字符串
+	return fmt.Sprintf("%x", hashBytes), nil
+}
diff --git a/app/utils/helper/validator.go b/app/utils/helper/validator.go
new file mode 100644
index 0000000..5a28fac
--- /dev/null
+++ b/app/utils/helper/validator.go
@@ -0,0 +1,11 @@
+package helper
+
+import "regexp"
+
+// chineseMobileFormMatcher 手机号格式正则匹配器
+var chineseMobileFormMatcher = regexp.MustCompile(`^1\d{10}$`)
+
+// IsMobileForm 手机号格式,不严谨的验证
+func IsMobileForm(mobile string) bool {
+	return chineseMobileFormMatcher.MatchString(mobile)
+}
diff --git a/app/utils/mapstructure/decode_hooks.go b/app/utils/mapstructure/decode_hooks.go
new file mode 100644
index 0000000..3a754ca
--- /dev/null
+++ b/app/utils/mapstructure/decode_hooks.go
@@ -0,0 +1,279 @@
+package mapstructure
+
+import (
+	"encoding"
+	"errors"
+	"fmt"
+	"net"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
+// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
+func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
+	// Create variables here so we can reference them with the reflect pkg
+	var f1 DecodeHookFuncType
+	var f2 DecodeHookFuncKind
+	var f3 DecodeHookFuncValue
+
+	// Fill in the variables into this interface and the rest is done
+	// automatically using the reflect package.
+	potential := []interface{}{f1, f2, f3}
+
+	v := reflect.ValueOf(h)
+	vt := v.Type()
+	for _, raw := range potential {
+		pt := reflect.ValueOf(raw).Type()
+		if vt.ConvertibleTo(pt) {
+			return v.Convert(pt).Interface()
+		}
+	}
+
+	return nil
+}
+
+// DecodeHookExec executes the given decode hook. This should be used
+// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
+// that took reflect.Kind instead of reflect.Type.
+func DecodeHookExec(
+	raw DecodeHookFunc,
+	from reflect.Value, to reflect.Value) (interface{}, error) {
+
+	switch f := typedDecodeHook(raw).(type) {
+	case DecodeHookFuncType:
+		return f(from.Type(), to.Type(), from.Interface())
+	case DecodeHookFuncKind:
+		return f(from.Kind(), to.Kind(), from.Interface())
+	case DecodeHookFuncValue:
+		return f(from, to)
+	default:
+		return nil, errors.New("invalid decode hook signature")
+	}
+}
+
+// ComposeDecodeHookFunc creates a single DecodeHookFunc that
+// automatically composes multiple DecodeHookFuncs.
+//
+// The composed funcs are called in order, with the result of the
+// previous transformation.
+func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
+	return func(f reflect.Value, t reflect.Value) (interface{}, error) {
+		var err error
+		data := f.Interface()
+
+		newFrom := f
+		for _, f1 := range fs {
+			data, err = DecodeHookExec(f1, newFrom, t)
+			if err != nil {
+				return nil, err
+			}
+			newFrom = reflect.ValueOf(data)
+		}
+
+		return data, nil
+	}
+}
+
+// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
+// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
+func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
+	return func(a, b reflect.Value) (interface{}, error) {
+		var allErrs string
+		var out interface{}
+		var err error
+
+		for _, f := range ff {
+			out, err = DecodeHookExec(f, a, b)
+			if err != nil {
+				allErrs += err.Error() + "\n"
+				continue
+			}
+
+			return out, nil
+		}
+
+		return nil, errors.New(allErrs)
+	}
+}
+
+// StringToSliceHookFunc returns a DecodeHookFunc that converts
+// string to []string by splitting on the given sep.
+func StringToSliceHookFunc(sep string) DecodeHookFunc {
+	return func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		if f != reflect.String || t != reflect.Slice {
+			return data, nil
+		}
+
+		raw := data.(string)
+		if raw == "" {
+			return []string{}, nil
+		}
+
+		return strings.Split(raw, sep), nil
+	}
+}
+
+// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
+// strings to time.Duration.
+func StringToTimeDurationHookFunc() DecodeHookFunc {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+		if f.Kind() != reflect.String {
+			return data, nil
+		}
+		if t != reflect.TypeOf(time.Duration(5)) {
+			return data, nil
+		}
+
+		// Convert it by parsing
+		return time.ParseDuration(data.(string))
+	}
+}
+
+// StringToIPHookFunc returns a DecodeHookFunc that converts
+// strings to net.IP
+func StringToIPHookFunc() DecodeHookFunc {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+		if f.Kind() != reflect.String {
+			return data, nil
+		}
+		if t != reflect.TypeOf(net.IP{}) {
+			return data, nil
+		}
+
+		// Convert it by parsing
+		ip := net.ParseIP(data.(string))
+		if ip == nil {
+			return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
+		}
+
+		return ip, nil
+	}
+}
+
+// StringToIPNetHookFunc returns a DecodeHookFunc that converts
+// strings to net.IPNet
+func StringToIPNetHookFunc() DecodeHookFunc {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+		if f.Kind() != reflect.String {
+			return data, nil
+		}
+		if t != reflect.TypeOf(net.IPNet{}) {
+			return data, nil
+		}
+
+		// Convert it by parsing
+		_, net, err := net.ParseCIDR(data.(string))
+		return net, err
+	}
+}
+
+// StringToTimeHookFunc returns a DecodeHookFunc that converts
+// strings to time.Time.
+func StringToTimeHookFunc(layout string) DecodeHookFunc {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+		if f.Kind() != reflect.String {
+			return data, nil
+		}
+		if t != reflect.TypeOf(time.Time{}) {
+			return data, nil
+		}
+
+		// Convert it by parsing
+		return time.Parse(layout, data.(string))
+	}
+}
+
+// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
+// the decoder.
+//
+// Note that this is significantly different from the WeaklyTypedInput option
+// of the DecoderConfig.
+func WeaklyTypedHook(
+	f reflect.Kind,
+	t reflect.Kind,
+	data interface{}) (interface{}, error) {
+	dataVal := reflect.ValueOf(data)
+	switch t {
+	case reflect.String:
+		switch f {
+		case reflect.Bool:
+			if dataVal.Bool() {
+				return "1", nil
+			}
+			return "0", nil
+		case reflect.Float32:
+			return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
+		case reflect.Int:
+			return strconv.FormatInt(dataVal.Int(), 10), nil
+		case reflect.Slice:
+			dataType := dataVal.Type()
+			elemKind := dataType.Elem().Kind()
+			if elemKind == reflect.Uint8 {
+				return string(dataVal.Interface().([]uint8)), nil
+			}
+		case reflect.Uint:
+			return strconv.FormatUint(dataVal.Uint(), 10), nil
+		}
+	}
+
+	return data, nil
+}
+
+func RecursiveStructToMapHookFunc() DecodeHookFunc {
+	return func(f reflect.Value, t reflect.Value) (interface{}, error) {
+		if f.Kind() != reflect.Struct {
+			return f.Interface(), nil
+		}
+
+		var i interface{} = struct{}{}
+		if t.Type() != reflect.TypeOf(&i).Elem() {
+			return f.Interface(), nil
+		}
+
+		m := make(map[string]interface{})
+		t.Set(reflect.ValueOf(m))
+
+		return f.Interface(), nil
+	}
+}
+
+// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
+// strings to the UnmarshalText function, when the target type
+// implements the encoding.TextUnmarshaler interface
+func TextUnmarshallerHookFunc() DecodeHookFuncType {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+		if f.Kind() != reflect.String {
+			return data, nil
+		}
+		result := reflect.New(t).Interface()
+		unmarshaller, ok := result.(encoding.TextUnmarshaler)
+		if !ok {
+			return data, nil
+		}
+		if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
+			return nil, err
+		}
+		return result, nil
+	}
+}
diff --git a/app/utils/mapstructure/decode_hooks_test.go b/app/utils/mapstructure/decode_hooks_test.go
new file mode 100644
index 0000000..bf02952
--- /dev/null
+++ b/app/utils/mapstructure/decode_hooks_test.go
@@ -0,0 +1,567 @@
+package mapstructure
+
+import (
+	"errors"
+	"math/big"
+	"net"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestComposeDecodeHookFunc(t *testing.T) {
+	f1 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return data.(string) + "foo", nil
+	}
+
+	f2 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return data.(string) + "bar", nil
+	}
+
+	f := ComposeDecodeHookFunc(f1, f2)
+
+	result, err := DecodeHookExec(
+		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
+	if err != nil {
+		t.Fatalf("bad: %s", err)
+	}
+	if result.(string) != "foobar" {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+func TestComposeDecodeHookFunc_err(t *testing.T) {
+	f1 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
+		return nil, errors.New("foo")
+	}
+
+	f2 := func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) {
+		panic("NOPE")
+	}
+
+	f := ComposeDecodeHookFunc(f1, f2)
+
+	_, err := DecodeHookExec(
+		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
+	if err.Error() != "foo" {
+		t.Fatalf("bad: %s", err)
+	}
+}
+
+func TestComposeDecodeHookFunc_kinds(t *testing.T) {
+	var f2From reflect.Kind
+
+	f1 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return int(42), nil
+	}
+
+	f2 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		f2From = f
+		return data, nil
+	}
+
+	f := ComposeDecodeHookFunc(f1, f2)
+
+	_, err := DecodeHookExec(
+		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
+	if err != nil {
+		t.Fatalf("bad: %s", err)
+	}
+	if f2From != reflect.Int {
+		t.Fatalf("bad: %#v", f2From)
+	}
+}
+
+func TestOrComposeDecodeHookFunc(t *testing.T) {
+	f1 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return data.(string) + "foo", nil
+	}
+
+	f2 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return data.(string) + "bar", nil
+	}
+
+	f := OrComposeDecodeHookFunc(f1, f2)
+
+	result, err := DecodeHookExec(
+		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
+	if err != nil {
+		t.Fatalf("bad: %s", err)
+	}
+	if result.(string) != "foo" {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+func TestOrComposeDecodeHookFunc_correctValueIsLast(t *testing.T) {
+	f1 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return nil, errors.New("f1 error")
+	}
+
+	f2 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return nil, errors.New("f2 error")
+	}
+
+	f3 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return data.(string) + "bar", nil
+	}
+
+	f := OrComposeDecodeHookFunc(f1, f2, f3)
+
+	result, err := DecodeHookExec(
+		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
+	if err != nil {
+		t.Fatalf("bad: %s", err)
+	}
+	if result.(string) != "bar" {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+func TestOrComposeDecodeHookFunc_err(t *testing.T) {
+	f1 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return nil, errors.New("f1 error")
+	}
+
+	f2 := func(
+		f reflect.Kind,
+		t reflect.Kind,
+		data interface{}) (interface{}, error) {
+		return nil, errors.New("f2 error")
+	}
+
+	f := OrComposeDecodeHookFunc(f1, f2)
+
+	_, err := DecodeHookExec(
+		f, reflect.ValueOf(""), reflect.ValueOf([]byte("")))
+	if err == nil {
+		t.Fatalf("bad: should return an error")
+	}
+	if err.Error() != "f1 error\nf2 error\n" {
+		t.Fatalf("bad: %s", err)
+	}
+}
+
+func TestComposeDecodeHookFunc_safe_nofuncs(t *testing.T) {
+	f := ComposeDecodeHookFunc()
+	type myStruct2 struct {
+		MyInt int
+	}
+
+	type myStruct1 struct {
+		Blah map[string]myStruct2
+	}
+
+	src := &myStruct1{Blah: map[string]myStruct2{
+		"test": {
+			MyInt: 1,
+		},
+	}}
+
+	dst := &myStruct1{}
+	dConf := &DecoderConfig{
+		Result:      dst,
+		ErrorUnused: true,
+		DecodeHook:  f,
+	}
+	d, err := NewDecoder(dConf)
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = d.Decode(src)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestStringToSliceHookFunc(t *testing.T) {
+	f := StringToSliceHookFunc(",")
+
+	strValue := reflect.ValueOf("42")
+	sliceValue := reflect.ValueOf([]byte("42"))
+	cases := []struct {
+		f, t   reflect.Value
+		result interface{}
+		err    bool
+	}{
+		{sliceValue, sliceValue, []byte("42"), false},
+		{strValue, strValue, "42", false},
+		{
+			reflect.ValueOf("foo,bar,baz"),
+			sliceValue,
+			[]string{"foo", "bar", "baz"},
+			false,
+		},
+		{
+			reflect.ValueOf(""),
+			sliceValue,
+			[]string{},
+			false,
+		},
+	}
+
+	for i, tc := range cases {
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
+
+func TestStringToTimeDurationHookFunc(t *testing.T) {
+	f := StringToTimeDurationHookFunc()
+
+	timeValue := reflect.ValueOf(time.Duration(5))
+	strValue := reflect.ValueOf("")
+	cases := []struct {
+		f, t   reflect.Value
+		result interface{}
+		err    bool
+	}{
+		{reflect.ValueOf("5s"), timeValue, 5 * time.Second, false},
+		{reflect.ValueOf("5"), timeValue, time.Duration(0), true},
+		{reflect.ValueOf("5"), strValue, "5", false},
+	}
+
+	for i, tc := range cases {
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
+
+func TestStringToTimeHookFunc(t *testing.T) {
+	strValue := reflect.ValueOf("5")
+	timeValue := reflect.ValueOf(time.Time{})
+	cases := []struct {
+		f, t   reflect.Value
+		layout string
+		result interface{}
+		err    bool
+	}{
+		{reflect.ValueOf("2006-01-02T15:04:05Z"), timeValue, time.RFC3339,
+			time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), false},
+		{strValue, timeValue, time.RFC3339, time.Time{}, true},
+		{strValue, strValue, time.RFC3339, "5", false},
+	}
+
+	for i, tc := range cases {
+		f := StringToTimeHookFunc(tc.layout)
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
+
+func TestStringToIPHookFunc(t *testing.T) {
+	strValue := reflect.ValueOf("5")
+	ipValue := reflect.ValueOf(net.IP{})
+	cases := []struct {
+		f, t   reflect.Value
+		result interface{}
+		err    bool
+	}{
+		{reflect.ValueOf("1.2.3.4"), ipValue,
+			net.IPv4(0x01, 0x02, 0x03, 0x04), false},
+		{strValue, ipValue, net.IP{}, true},
+		{strValue, strValue, "5", false},
+	}
+
+	for i, tc := range cases {
+		f := StringToIPHookFunc()
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
+
+func TestStringToIPNetHookFunc(t *testing.T) {
+	strValue := reflect.ValueOf("5")
+	ipNetValue := reflect.ValueOf(net.IPNet{})
+	var nilNet *net.IPNet = nil
+
+	cases := []struct {
+		f, t   reflect.Value
+		result interface{}
+		err    bool
+	}{
+		{reflect.ValueOf("1.2.3.4/24"), ipNetValue,
+			&net.IPNet{
+				IP:   net.IP{0x01, 0x02, 0x03, 0x00},
+				Mask: net.IPv4Mask(0xff, 0xff, 0xff, 0x00),
+			}, false},
+		{strValue, ipNetValue, nilNet, true},
+		{strValue, strValue, "5", false},
+	}
+
+	for i, tc := range cases {
+		f := StringToIPNetHookFunc()
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
+
+func TestWeaklyTypedHook(t *testing.T) {
+	var f DecodeHookFunc = WeaklyTypedHook
+
+	strValue := reflect.ValueOf("")
+	cases := []struct {
+		f, t   reflect.Value
+		result interface{}
+		err    bool
+	}{
+		// TO STRING
+		{
+			reflect.ValueOf(false),
+			strValue,
+			"0",
+			false,
+		},
+
+		{
+			reflect.ValueOf(true),
+			strValue,
+			"1",
+			false,
+		},
+
+		{
+			reflect.ValueOf(float32(7)),
+			strValue,
+			"7",
+			false,
+		},
+
+		{
+			reflect.ValueOf(int(7)),
+			strValue,
+			"7",
+			false,
+		},
+
+		{
+			reflect.ValueOf([]uint8("foo")),
+			strValue,
+			"foo",
+			false,
+		},
+
+		{
+			reflect.ValueOf(uint(7)),
+			strValue,
+			"7",
+			false,
+		},
+	}
+
+	for i, tc := range cases {
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
+
+func TestStructToMapHookFuncTabled(t *testing.T) {
+	var f DecodeHookFunc = RecursiveStructToMapHookFunc()
+
+	type b struct {
+		TestKey string
+	}
+
+	type a struct {
+		Sub b
+	}
+
+	testStruct := a{
+		Sub: b{
+			TestKey: "testval",
+		},
+	}
+
+	testMap := map[string]interface{}{
+		"Sub": map[string]interface{}{
+			"TestKey": "testval",
+		},
+	}
+
+	cases := []struct {
+		name     string
+		receiver interface{}
+		input    interface{}
+		expected interface{}
+		err      bool
+	}{
+		{
+			"map receiver",
+			func() interface{} {
+				var res map[string]interface{}
+				return &res
+			}(),
+			testStruct,
+			&testMap,
+			false,
+		},
+		{
+			"interface receiver",
+			func() interface{} {
+				var res interface{}
+				return &res
+			}(),
+			testStruct,
+			func() interface{} {
+				var exp interface{} = testMap
+				return &exp
+			}(),
+			false,
+		},
+		{
+			"slice receiver errors",
+			func() interface{} {
+				var res []string
+				return &res
+			}(),
+			testStruct,
+			new([]string),
+			true,
+		},
+		{
+			"slice to slice - no change",
+			func() interface{} {
+				var res []string
+				return &res
+			}(),
+			[]string{"a", "b"},
+			&[]string{"a", "b"},
+			false,
+		},
+		{
+			"string to string - no change",
+			func() interface{} {
+				var res string
+				return &res
+			}(),
+			"test",
+			func() *string {
+				s := "test"
+				return &s
+			}(),
+			false,
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			cfg := &DecoderConfig{
+				DecodeHook: f,
+				Result:     tc.receiver,
+			}
+
+			d, err := NewDecoder(cfg)
+			if err != nil {
+				t.Fatalf("unexpected err %#v", err)
+			}
+
+			err = d.Decode(tc.input)
+			if tc.err != (err != nil) {
+				t.Fatalf("expected err %#v", err)
+			}
+
+			if !reflect.DeepEqual(tc.expected, tc.receiver) {
+				t.Fatalf("expected %#v, got %#v",
+					tc.expected, tc.receiver)
+			}
+		})
+
+	}
+}
+
+func TestTextUnmarshallerHookFunc(t *testing.T) {
+	cases := []struct {
+		f, t   reflect.Value
+		result interface{}
+		err    bool
+	}{
+		{reflect.ValueOf("42"), reflect.ValueOf(big.Int{}), big.NewInt(42), false},
+		{reflect.ValueOf("invalid"), reflect.ValueOf(big.Int{}), nil, true},
+		{reflect.ValueOf("5"), reflect.ValueOf("5"), "5", false},
+	}
+
+	for i, tc := range cases {
+		f := TextUnmarshallerHookFunc()
+		actual, err := DecodeHookExec(f, tc.f, tc.t)
+		if tc.err != (err != nil) {
+			t.Fatalf("case %d: expected err %#v", i, tc.err)
+		}
+		if !reflect.DeepEqual(actual, tc.result) {
+			t.Fatalf(
+				"case %d: expected %#v, got %#v",
+				i, tc.result, actual)
+		}
+	}
+}
diff --git a/app/utils/mapstructure/error.go b/app/utils/mapstructure/error.go
new file mode 100644
index 0000000..47a99e5
--- /dev/null
+++ b/app/utils/mapstructure/error.go
@@ -0,0 +1,50 @@
+package mapstructure
+
+import (
+	"errors"
+	"fmt"
+	"sort"
+	"strings"
+)
+
+// Error implements the error interface and can represents multiple
+// errors that occur in the course of a single decode.
+type Error struct {
+	Errors []string
+}
+
+func (e *Error) Error() string {
+	points := make([]string, len(e.Errors))
+	for i, err := range e.Errors {
+		points[i] = fmt.Sprintf("* %s", err)
+	}
+
+	sort.Strings(points)
+	return fmt.Sprintf(
+		"%d error(s) decoding:\n\n%s",
+		len(e.Errors), strings.Join(points, "\n"))
+}
+
+// WrappedErrors implements the errwrap.Wrapper interface to make this
+// return value more useful with the errwrap and go-multierror libraries.
+func (e *Error) WrappedErrors() []error {
+	if e == nil {
+		return nil
+	}
+
+	result := make([]error, len(e.Errors))
+	for i, e := range e.Errors {
+		result[i] = errors.New(e)
+	}
+
+	return result
+}
+
+func appendErrors(errors []string, err error) []string {
+	switch e := err.(type) {
+	case *Error:
+		return append(errors, e.Errors...)
+	default:
+		return append(errors, e.Error())
+	}
+}
diff --git a/app/utils/mapstructure/mapstructure.go b/app/utils/mapstructure/mapstructure.go
new file mode 100644
index 0000000..0d26c75
--- /dev/null
+++ b/app/utils/mapstructure/mapstructure.go
@@ -0,0 +1,1386 @@
+package mapstructure
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"reflect"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+// DecodeHookFunc is the callback function that can be used for
+// data transformations. See "DecodeHook" in the DecoderConfig
+// struct.
+//
+// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or
+// DecodeHookFuncValue.
+// Values are a superset of Types (Values can return types), and Types are a
+// superset of Kinds (Types can return Kinds) and are generally a richer thing
+// to use, but Kinds are simpler if you only need those.
+//
+// The reason DecodeHookFunc is multi-typed is for backwards compatibility:
+// we started with Kinds and then realized Types were the better solution,
+// but have a promise to not break backwards compat so we now support
+// both.
+type DecodeHookFunc interface{}
+
+// DecodeHookFuncType is a DecodeHookFunc which has complete information about
+// the source and target types.
+type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error)
+
+// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the
+// source and target types.
+type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
+
+// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target
+// values.
+type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
+
+// DecoderConfig is the configuration that is used to create a new decoder
+// and allows customization of various aspects of decoding.
+type DecoderConfig struct {
+	// DecodeHook, if set, will be called before any decoding and any
+	// type conversion (if WeaklyTypedInput is on). This lets you modify
+	// the values before they're set down onto the resulting struct. The
+	// DecodeHook is called for every map and value in the input. This means
+	// that if a struct has embedded fields with squash tags the decode hook
+	// is called only once with all of the input data, not once for each
+	// embedded struct.
+	//
+	// If an error is returned, the entire decode will fail with that error.
+	DecodeHook DecodeHookFunc
+
+	// If ErrorUnused is true, then it is an error for there to exist
+	// keys in the original map that were unused in the decoding process
+	// (extra keys).
+	ErrorUnused bool
+
+	// If ErrorUnset is true, then it is an error for there to exist
+	// fields in the result that were not set in the decoding process
+	// (extra fields). This only applies to decoding to a struct. This
+	// will affect all nested structs as well.
+	ErrorUnset bool
+
+	// ZeroFields, if set to true, will zero fields before writing them.
+	// For example, a map will be emptied before decoded values are put in
+	// it. If this is false, a map will be merged.
+	ZeroFields bool
+
+	// If WeaklyTypedInput is true, the decoder will make the following
+	// "weak" conversions:
+	//
+	//   - bools to string (true = "1", false = "0")
+	//   - numbers to string (base 10)
+	//   - bools to int/uint (true = 1, false = 0)
+	//   - strings to int/uint (base implied by prefix)
+	//   - int to bool (true if value != 0)
+	//   - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F,
+	//     FALSE, false, False. Anything else is an error)
+	//   - empty array = empty map and vice versa
+	//   - negative numbers to overflowed uint values (base 10)
+	//   - slice of maps to a merged map
+	//   - single values are converted to slices if required. Each
+	//     element is weakly decoded. For example: "4" can become []int{4}
+	//     if the target type is an int slice.
+	//
+	WeaklyTypedInput bool
+
+	// Squash will squash embedded structs.  A squash tag may also be
+	// added to an individual struct field using a tag.  For example:
+	//
+	//  type Parent struct {
+	//      Child `mapstructure:",squash"`
+	//  }
+	Squash bool
+
+	// Metadata is the struct that will contain extra metadata about
+	// the decoding. If this is nil, then no metadata will be tracked.
+	Metadata *Metadata
+
+	// Result is a pointer to the struct that will contain the decoded
+	// value.
+	Result interface{}
+
+	// The tag name that mapstructure reads for field names. This
+	// defaults to "mapstructure"
+	TagName string
+
+	// IgnoreUntaggedFields ignores all struct fields without explicit
+	// TagName, comparable to `mapstructure:"-"` as default behaviour.
+	IgnoreUntaggedFields bool
+
+	// MatchName is the function used to match the map key to the struct
+	// field name or tag. Defaults to `strings.EqualFold`. This can be used
+	// to implement case-sensitive tag values, support snake casing, etc.
+	MatchName func(mapKey, fieldName string) bool
+}
+
+// A Decoder takes a raw interface value and turns it into structured
+// data, keeping track of rich error information along the way in case
+// anything goes wrong. Unlike the basic top-level Decode method, you can
+// more finely control how the Decoder behaves using the DecoderConfig
+// structure. The top-level Decode method is just a convenience that sets
+// up the most basic Decoder.
+type Decoder struct {
+	config *DecoderConfig
+}
+
+// Metadata contains information about decoding a structure that
+// is tedious or difficult to get otherwise.
+type Metadata struct {
+	// Keys are the keys of the structure which were successfully decoded
+	Keys []string
+
+	// Unused is a slice of keys that were found in the raw value but
+	// weren't decoded since there was no matching field in the result interface
+	Unused []string
+
+	// Unset is a slice of field names that were found in the result interface
+	// but weren't set in the decoding process since there was no matching value
+	// in the input
+	Unset []string
+}
+
+// Decode takes an input structure and uses reflection to translate it to
+// the output structure. output must be a pointer to a map or struct.
+func Decode(input interface{}, output interface{}) error {
+	config := &DecoderConfig{
+		Metadata: nil,
+		Result:   output,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(input)
+}
+
+// WeakDecode is the same as Decode but is shorthand to enable
+// WeaklyTypedInput. See DecoderConfig for more info.
+func WeakDecode(input, output interface{}) error {
+	config := &DecoderConfig{
+		Metadata:         nil,
+		Result:           output,
+		WeaklyTypedInput: true,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(input)
+}
+
+// DecodeMetadata is the same as Decode, but is shorthand to
+// enable metadata collection. See DecoderConfig for more info.
+func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
+	config := &DecoderConfig{
+		Metadata: metadata,
+		Result:   output,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(input)
+}
+
+// WeakDecodeMetadata is the same as Decode, but is shorthand to
+// enable both WeaklyTypedInput and metadata collection. See
+// DecoderConfig for more info.
+func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
+	config := &DecoderConfig{
+		Metadata:         metadata,
+		Result:           output,
+		WeaklyTypedInput: true,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(input)
+}
+
+// NewDecoder returns a new decoder for the given configuration. Once
+// a decoder has been returned, the same configuration must not be used
+// again.
+func NewDecoder(config *DecoderConfig) (*Decoder, error) {
+	val := reflect.ValueOf(config.Result)
+	if val.Kind() != reflect.Ptr {
+		return nil, errors.New("result must be a pointer")
+	}
+
+	val = val.Elem()
+	if !val.CanAddr() {
+		return nil, errors.New("result must be addressable (a pointer)")
+	}
+
+	if config.Metadata != nil {
+		if config.Metadata.Keys == nil {
+			config.Metadata.Keys = make([]string, 0)
+		}
+
+		if config.Metadata.Unused == nil {
+			config.Metadata.Unused = make([]string, 0)
+		}
+
+		if config.Metadata.Unset == nil {
+			config.Metadata.Unset = make([]string, 0)
+		}
+	}
+
+	if config.TagName == "" {
+		config.TagName = "mapstructure"
+	}
+
+	if config.MatchName == nil {
+		config.MatchName = strings.EqualFold
+	}
+
+	result := &Decoder{
+		config: config,
+	}
+
+	return result, nil
+}
+
+// Decode decodes the given raw interface to the target pointer specified
+// by the configuration.
+func (d *Decoder) Decode(input interface{}) error {
+	return d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
+}
+
+// Decodes an unknown data type into a specific reflection value.
+func (d *Decoder) decode(name string, input interface{}, 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 input == nil {
+		// If the data is nil, then we don't set anything, unless ZeroFields is set
+		// to true.
+		if d.config.ZeroFields {
+			outVal.Set(reflect.Zero(outVal.Type()))
+
+			if d.config.Metadata != nil && name != "" {
+				d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
+			}
+		}
+		return 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()))
+		if d.config.Metadata != nil && name != "" {
+			d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
+		}
+		return nil
+	}
+
+	if d.config.DecodeHook != nil {
+		// We have a DecodeHook, so let's pre-process the input.
+		var err error
+		input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal)
+		if err != nil {
+			return fmt.Errorf("error decoding '%s': %s", name, err)
+		}
+	}
+
+	var err error
+	outputKind := getKind(outVal)
+	addMetaKey := true
+	switch outputKind {
+	case reflect.Bool:
+		err = d.decodeBool(name, input, outVal)
+	case reflect.Interface:
+		err = d.decodeBasic(name, input, outVal)
+	case reflect.String:
+		err = d.decodeString(name, input, outVal)
+	case reflect.Int:
+		err = d.decodeInt(name, input, outVal)
+	case reflect.Uint:
+		err = d.decodeUint(name, input, outVal)
+	case reflect.Float32:
+		err = d.decodeFloat(name, input, outVal)
+	case reflect.Struct:
+		err = d.decodeStruct(name, input, outVal)
+	case reflect.Map:
+		err = d.decodeMap(name, input, outVal)
+	case reflect.Ptr:
+		addMetaKey, err = d.decodePtr(name, input, outVal)
+	case reflect.Slice:
+		err = d.decodeSlice(name, input, outVal)
+	case reflect.Array:
+		err = d.decodeArray(name, input, outVal)
+	case reflect.Func:
+		err = d.decodeFunc(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)
+	}
+
+	// If we reached here, then we successfully decoded SOMETHING, so
+	// mark the key as used if we're tracking metainput.
+	if addMetaKey && d.config.Metadata != nil && name != "" {
+		d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
+	}
+
+	return err
+}
+
+// This decodes a basic type (bool, int, string, etc.) and sets the
+// value to "data" of that type.
+func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
+	if val.IsValid() && val.Elem().IsValid() {
+		elem := val.Elem()
+
+		// If we can't address this element, then its not writable. Instead,
+		// we make a copy of the value (which is a pointer and therefore
+		// writable), decode into that, and replace the whole value.
+		copied := false
+		if !elem.CanAddr() {
+			copied = true
+
+			// Make *T
+			copy := reflect.New(elem.Type())
+
+			// *T = elem
+			copy.Elem().Set(elem)
+
+			// Set elem so we decode into it
+			elem = copy
+		}
+
+		// Decode. If we have an error then return. We also return right
+		// away if we're not a copy because that means we decoded directly.
+		if err := d.decode(name, data, elem); err != nil || !copied {
+			return err
+		}
+
+		// If we're a copy, we need to set te final result
+		val.Set(elem.Elem())
+		return nil
+	}
+
+	dataVal := reflect.ValueOf(data)
+
+	// If the input data is a pointer, and the assigned type is the dereference
+	// of that exact pointer, then indirect it so that we can assign it.
+	// Example: *string to string
+	if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() {
+		dataVal = reflect.Indirect(dataVal)
+	}
+
+	if !dataVal.IsValid() {
+		dataVal = reflect.Zero(val.Type())
+	}
+
+	dataValType := dataVal.Type()
+	if !dataValType.AssignableTo(val.Type()) {
+		return fmt.Errorf(
+			"'%s' expected type '%s', got '%s'",
+			name, val.Type(), dataValType)
+	}
+
+	val.Set(dataVal)
+	return nil
+}
+
+func (d *Decoder) decodeString(name string, data interface{}, 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 && d.config.WeaklyTypedInput:
+		if dataVal.Bool() {
+			val.SetString("1")
+		} else {
+			val.SetString("0")
+		}
+	case dataKind == reflect.Int && d.config.WeaklyTypedInput:
+		val.SetString(strconv.FormatInt(dataVal.Int(), 10))
+	case dataKind == reflect.Uint && d.config.WeaklyTypedInput:
+		val.SetString(strconv.FormatUint(dataVal.Uint(), 10))
+	case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:
+		val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64))
+	case dataKind == reflect.Slice && d.config.WeaklyTypedInput,
+		dataKind == reflect.Array && d.config.WeaklyTypedInput:
+		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', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeInt(name string, data interface{}, 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 && d.config.WeaklyTypedInput:
+		if dataVal.Bool() {
+			val.SetInt(1)
+		} else {
+			val.SetInt(0)
+		}
+	case dataKind == reflect.String && d.config.WeaklyTypedInput:
+		str := dataVal.String()
+		if str == "" {
+			str = "0"
+		}
+
+		i, err := strconv.ParseInt(str, 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', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeUint(name string, data interface{}, 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 && !d.config.WeaklyTypedInput {
+			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 && !d.config.WeaklyTypedInput {
+			return fmt.Errorf("cannot parse '%s', %f overflows uint",
+				name, f)
+		}
+		val.SetUint(uint64(f))
+	case dataKind == reflect.Bool && d.config.WeaklyTypedInput:
+		if dataVal.Bool() {
+			val.SetUint(1)
+		} else {
+			val.SetUint(0)
+		}
+	case dataKind == reflect.String && d.config.WeaklyTypedInput:
+		str := dataVal.String()
+		if str == "" {
+			str = "0"
+		}
+
+		i, err := strconv.ParseUint(str, 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 := strconv.ParseUint(string(jn), 0, 64)
+		if err != nil {
+			return fmt.Errorf(
+				"error decoding json.Number into %s: %s", name, err)
+		}
+		val.SetUint(i)
+	default:
+		return fmt.Errorf(
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
+	dataVal := reflect.Indirect(reflect.ValueOf(data))
+	dataKind := getKind(dataVal)
+
+	switch {
+	case dataKind == reflect.Bool:
+		val.SetBool(dataVal.Bool())
+	case dataKind == reflect.Int && d.config.WeaklyTypedInput:
+		val.SetBool(dataVal.Int() != 0)
+	case dataKind == reflect.Uint && d.config.WeaklyTypedInput:
+		val.SetBool(dataVal.Uint() != 0)
+	case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:
+		val.SetBool(dataVal.Float() != 0)
+	case dataKind == reflect.String && d.config.WeaklyTypedInput:
+		b, err := strconv.ParseBool(dataVal.String())
+		if err == nil {
+			val.SetBool(b)
+		} else if dataVal.String() == "" {
+			val.SetBool(false)
+		} else {
+			return fmt.Errorf("cannot parse '%s' as bool: %s", name, err)
+		}
+	default:
+		return fmt.Errorf(
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeFloat(name string, data interface{}, 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 && d.config.WeaklyTypedInput:
+		if dataVal.Bool() {
+			val.SetFloat(1)
+		} else {
+			val.SetFloat(0)
+		}
+	case dataKind == reflect.String && d.config.WeaklyTypedInput:
+		str := dataVal.String()
+		if str == "" {
+			str = "0"
+		}
+
+		f, err := strconv.ParseFloat(str, 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', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error {
+	valType := val.Type()
+	valKeyType := valType.Key()
+	valElemType := valType.Elem()
+
+	// By default we overwrite keys in the current map
+	valMap := val
+
+	// If the map is nil or we're purposely zeroing fields, make a new map
+	if valMap.IsNil() || d.config.ZeroFields {
+		// Make a new map to hold our result
+		mapType := reflect.MapOf(valKeyType, valElemType)
+		valMap = reflect.MakeMap(mapType)
+	}
+
+	// Check input type and based on the input type jump to the proper func
+	dataVal := reflect.Indirect(reflect.ValueOf(data))
+	switch dataVal.Kind() {
+	case reflect.Map:
+		return d.decodeMapFromMap(name, dataVal, val, valMap)
+
+	case reflect.Struct:
+		return d.decodeMapFromStruct(name, dataVal, val, valMap, data)
+
+	case reflect.Array, reflect.Slice:
+		if d.config.WeaklyTypedInput {
+			return d.decodeMapFromSlice(name, dataVal, val, valMap)
+		}
+
+		fallthrough
+
+	default:
+		return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
+	}
+}
+
+func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
+	// Special case for BC reasons (covered by tests)
+	if dataVal.Len() == 0 {
+		val.Set(valMap)
+		return nil
+	}
+
+	for i := 0; i < dataVal.Len(); i++ {
+		err := d.decode(
+			name+"["+strconv.Itoa(i)+"]",
+			dataVal.Index(i).Interface(), val)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {
+	valType := val.Type()
+	valKeyType := valType.Key()
+	valElemType := valType.Elem()
+
+	// Accumulate errors
+	errors := make([]string, 0)
+
+	// If the input data is empty, then we just match what the input data is.
+	if dataVal.Len() == 0 {
+		if dataVal.IsNil() {
+			if !val.IsNil() {
+				val.Set(dataVal)
+			}
+		} else {
+			// Set to empty allocated value
+			val.Set(valMap)
+		}
+
+		return nil
+	}
+
+	for _, k := range dataVal.MapKeys() {
+		fieldName := name + "[" + k.String() + "]"
+
+		// First decode the key into the proper type
+		currentKey := reflect.Indirect(reflect.New(valKeyType))
+		if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {
+			errors = appendErrors(errors, err)
+			continue
+		}
+
+		// Next decode the data into the proper type
+		v := dataVal.MapIndex(k).Interface()
+		currentVal := reflect.Indirect(reflect.New(valElemType))
+		if err := d.decode(fieldName, v, currentVal); err != nil {
+			errors = appendErrors(errors, err)
+			continue
+		}
+
+		valMap.SetMapIndex(currentKey, currentVal)
+	}
+
+	// Set the built up map to the value
+	val.Set(valMap)
+
+	// If we had errors, return those
+	if len(errors) > 0 {
+		return &Error{errors}
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value, inData interface{}) error {
+	typ := dataVal.Type()
+
+	for i := 0; i < typ.NumField(); i++ {
+		// Get the StructField first since this is a cheap operation. If the
+		// field is unexported, then ignore it.
+		f := typ.Field(i)
+		if f.PkgPath != "" {
+			continue
+		}
+
+		// Next get the actual value of this field and verify it is assignable
+		// to the map value.
+		v := dataVal.Field(i)
+		if !v.Type().AssignableTo(valMap.Type().Elem()) {
+			return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
+		}
+
+		tagValue := f.Tag.Get(d.config.TagName)
+		keyName := f.Name
+
+		if tagValue == "" && d.config.IgnoreUntaggedFields {
+			continue
+		}
+
+		// If Squash is set in the config, we squash the field down.
+		squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
+
+		v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
+
+		// Determine the name of the key in the map
+		if index := strings.Index(tagValue, ","); index != -1 {
+			if tagValue[:index] == "-" {
+				continue
+			}
+			// If "omitempty" is specified in the tag, it ignores empty values.
+			if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
+				continue
+			}
+
+			// If "squash" is specified in the tag, we squash the field down.
+			squash = squash || strings.Index(tagValue[index+1:], "squash") != -1
+			if squash {
+				// When squashing, the embedded type can be a pointer to a struct.
+				if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
+					v = v.Elem()
+				}
+
+				// The final type must be a struct
+				if v.Kind() != reflect.Struct {
+					return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
+				}
+			}
+			if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
+				keyName = keyNameTagValue
+			}
+		} else if len(tagValue) > 0 {
+			if tagValue == "-" {
+				continue
+			}
+			keyName = tagValue
+		}
+
+		switch v.Kind() {
+		// this is an embedded struct, so handle it differently
+		case reflect.Struct:
+			x := reflect.New(v.Type())
+			x.Elem().Set(v)
+
+			vType := valMap.Type()
+			vKeyType := vType.Key()
+			vElemType := vType.Elem()
+			mType := reflect.MapOf(vKeyType, vElemType)
+			vMap := reflect.MakeMap(mType)
+
+			// Creating a pointer to a map so that other methods can completely
+			// overwrite the map if need be (looking at you decodeMapFromMap). The
+			// indirection allows the underlying map to be settable (CanSet() == true)
+			// where as reflect.MakeMap returns an unsettable map.
+			addrVal := reflect.New(vMap.Type())
+			reflect.Indirect(addrVal).Set(vMap)
+
+			err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal))
+			if err != nil {
+				return err
+			}
+
+			// the underlying map may have been completely overwritten so pull
+			// it indirectly out of the enclosing value.
+			vMap = reflect.Indirect(addrVal)
+
+			if squash {
+				for _, k := range vMap.MapKeys() {
+					valMap.SetMapIndex(k, vMap.MapIndex(k))
+				}
+			} else {
+				valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
+			}
+
+		default:
+			valMap.SetMapIndex(reflect.ValueOf(keyName), v)
+		}
+
+	}
+
+	if val.CanAddr() {
+		val.Set(valMap)
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodePtr(name string, data interface{}, 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() || d.config.ZeroFields {
+			realVal = reflect.New(valElemType)
+		}
+
+		if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {
+			// 报错情况下依旧设置指针
+			val.Set(realVal)
+			return false, err
+		}
+
+		val.Set(realVal)
+	} else {
+		if err := d.decode(name, data, reflect.Indirect(val)); err != nil {
+			return false, err
+		}
+	}
+	return false, nil
+}
+
+func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error {
+	// Create an element of the concrete (non pointer) type and decode
+	// into that. Then set the value of the pointer to this type.
+	dataVal := reflect.Indirect(reflect.ValueOf(data))
+	if val.Type() != dataVal.Type() {
+		return fmt.Errorf(
+			"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
+			name, val.Type(), dataVal.Type(), data)
+	}
+	val.Set(dataVal)
+	return nil
+}
+
+func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error {
+	dataVal := reflect.Indirect(reflect.ValueOf(data))
+	dataValKind := dataVal.Kind()
+	valType := val.Type()
+	valElemType := valType.Elem()
+	sliceType := reflect.SliceOf(valElemType)
+
+	// If we have a non array/slice type then we first attempt to convert.
+	if dataValKind != reflect.Array && dataValKind != reflect.Slice {
+		if d.config.WeaklyTypedInput {
+			switch {
+			// Slice and array we use the normal logic
+			case dataValKind == reflect.Slice, dataValKind == reflect.Array:
+				break
+
+			// Empty maps turn into empty slices
+			case dataValKind == reflect.Map:
+				if dataVal.Len() == 0 {
+					val.Set(reflect.MakeSlice(sliceType, 0, 0))
+					return nil
+				}
+				// Create slice of maps of other sizes
+				return d.decodeSlice(name, []interface{}{data}, val)
+
+			case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
+				return d.decodeSlice(name, []byte(dataVal.String()), val)
+
+			// All other types we try to convert to the slice type
+			// and "lift" it into it. i.e. a string becomes a string slice.
+			default:
+				// Just re-try this function with data as a slice.
+				return d.decodeSlice(name, []interface{}{data}, val)
+			}
+		}
+
+		return fmt.Errorf(
+			"'%s': source data must be an array or slice, got %s", name, dataValKind)
+	}
+
+	// If the input value is nil, then don't allocate since empty != nil
+	if dataValKind != reflect.Array && dataVal.IsNil() {
+		return nil
+	}
+
+	valSlice := val
+	if valSlice.IsNil() || d.config.ZeroFields {
+		// Make a new slice to hold our result, same size as the original data.
+		valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
+	}
+
+	// Accumulate any errors
+	errors := make([]string, 0)
+
+	for i := 0; i < dataVal.Len(); i++ {
+		currentData := dataVal.Index(i).Interface()
+		for valSlice.Len() <= i {
+			valSlice = reflect.Append(valSlice, reflect.Zero(valElemType))
+		}
+		currentField := valSlice.Index(i)
+
+		fieldName := name + "[" + strconv.Itoa(i) + "]"
+		if err := d.decode(fieldName, currentData, currentField); err != nil {
+			errors = appendErrors(errors, err)
+		}
+	}
+
+	// Finally, set the value to the slice we built up
+	val.Set(valSlice)
+
+	// If there were errors, we return those
+	if len(errors) > 0 {
+		return &Error{errors}
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error {
+	dataVal := reflect.Indirect(reflect.ValueOf(data))
+	dataValKind := dataVal.Kind()
+	valType := val.Type()
+	valElemType := valType.Elem()
+	arrayType := reflect.ArrayOf(valType.Len(), valElemType)
+
+	valArray := val
+
+	if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields {
+		// Check input type
+		if dataValKind != reflect.Array && dataValKind != reflect.Slice {
+			if d.config.WeaklyTypedInput {
+				switch {
+				// Empty maps turn into empty arrays
+				case dataValKind == reflect.Map:
+					if dataVal.Len() == 0 {
+						val.Set(reflect.Zero(arrayType))
+						return nil
+					}
+
+				// All other types we try to convert to the array type
+				// and "lift" it into it. i.e. a string becomes a string array.
+				default:
+					// Just re-try this function with data as a slice.
+					return d.decodeArray(name, []interface{}{data}, val)
+				}
+			}
+
+			return fmt.Errorf(
+				"'%s': source data must be an array or slice, got %s", name, dataValKind)
+
+		}
+		if dataVal.Len() > arrayType.Len() {
+			return fmt.Errorf(
+				"'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len())
+
+		}
+
+		// Make a new array to hold our result, same size as the original data.
+		valArray = reflect.New(arrayType).Elem()
+	}
+
+	// Accumulate any errors
+	errors := make([]string, 0)
+
+	for i := 0; i < dataVal.Len(); i++ {
+		currentData := dataVal.Index(i).Interface()
+		currentField := valArray.Index(i)
+
+		fieldName := name + "[" + strconv.Itoa(i) + "]"
+		if err := d.decode(fieldName, currentData, currentField); err != nil {
+			errors = appendErrors(errors, err)
+		}
+	}
+
+	// Finally, set the value to the array we built up
+	val.Set(valArray)
+
+	// If there were errors, we return those
+	if len(errors) > 0 {
+		return &Error{errors}
+	}
+
+	return nil
+}
+
+func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error {
+	dataVal := reflect.Indirect(reflect.ValueOf(data))
+
+	// If the type of the value to write to and the data match directly,
+	// then we just set it directly instead of recursing into the structure.
+	if dataVal.Type() == val.Type() {
+		val.Set(dataVal)
+		return nil
+	}
+
+	dataValKind := dataVal.Kind()
+	switch dataValKind {
+	case reflect.Map:
+		return d.decodeStructFromMap(name, dataVal, val)
+
+	case reflect.Struct:
+		// Not the most efficient way to do this but we can optimize later if
+		// we want to. To convert from struct to struct we go to map first
+		// as an intermediary.
+
+		// Make a new map to hold our result
+		mapType := reflect.TypeOf((map[string]interface{})(nil))
+		mval := reflect.MakeMap(mapType)
+
+		// Creating a pointer to a map so that other methods can completely
+		// overwrite the map if need be (looking at you decodeMapFromMap). The
+		// indirection allows the underlying map to be settable (CanSet() == true)
+		// where as reflect.MakeMap returns an unsettable map.
+		addrVal := reflect.New(mval.Type())
+
+		reflect.Indirect(addrVal).Set(mval)
+		if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval, data); err != nil {
+			return err
+		}
+
+		result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val)
+		return result
+
+	default:
+		return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
+	}
+}
+
+func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {
+	dataValType := dataVal.Type()
+	if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
+		return fmt.Errorf(
+			"'%s' needs a map with string keys, has '%s' keys",
+			name, dataValType.Key().Kind())
+	}
+
+	dataValKeys := make(map[reflect.Value]struct{})
+	dataValKeysUnused := make(map[interface{}]struct{})
+	for _, dataValKey := range dataVal.MapKeys() {
+		dataValKeys[dataValKey] = struct{}{}
+		dataValKeysUnused[dataValKey.Interface()] = struct{}{}
+	}
+
+	targetValKeysUnused := make(map[interface{}]struct{})
+	errors := make([]string, 0)
+
+	// This slice will keep track of all the structs we'll be decoding.
+	// There can be more than one struct if there are embedded structs
+	// that are squashed.
+	structs := make([]reflect.Value, 1, 5)
+	structs[0] = val
+
+	// Compile the list of all the fields that we're going to be decoding
+	// from all the structs.
+	type field struct {
+		field reflect.StructField
+		val   reflect.Value
+	}
+
+	// remainField is set to a valid field set with the "remain" tag if
+	// we are keeping track of remaining values.
+	var remainField *field
+
+	fields := []field{}
+	for len(structs) > 0 {
+		structVal := structs[0]
+		structs = structs[1:]
+
+		structType := structVal.Type()
+
+		for i := 0; i < structType.NumField(); i++ {
+			fieldType := structType.Field(i)
+			fieldVal := structVal.Field(i)
+			if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {
+				// Handle embedded struct pointers as embedded structs.
+				fieldVal = fieldVal.Elem()
+			}
+
+			// If "squash" is specified in the tag, we squash the field down.
+			squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous
+			remain := false
+
+			// We always parse the tags cause we're looking for other tags too
+			tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
+			for _, tag := range tagParts[1:] {
+				if tag == "squash" {
+					squash = true
+					break
+				}
+
+				if tag == "remain" {
+					remain = true
+					break
+				}
+			}
+
+			if squash {
+				if fieldVal.Kind() != reflect.Struct {
+					errors = appendErrors(errors,
+						fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
+				} else {
+					structs = append(structs, fieldVal)
+				}
+				continue
+			}
+
+			// Build our field
+			if remain {
+				remainField = &field{fieldType, fieldVal}
+			} else {
+				// Normal struct field, store it away
+				fields = append(fields, field{fieldType, fieldVal})
+			}
+		}
+	}
+
+	// for fieldType, field := range fields {
+	for _, f := range fields {
+		field, fieldValue := f.field, f.val
+		fieldName := field.Name
+
+		tagValue := field.Tag.Get(d.config.TagName)
+		tagValue = strings.SplitN(tagValue, ",", 2)[0]
+		if tagValue != "" {
+			fieldName = tagValue
+		}
+
+		rawMapKey := reflect.ValueOf(fieldName)
+		rawMapVal := dataVal.MapIndex(rawMapKey)
+		if !rawMapVal.IsValid() {
+			// Do a slower search by iterating over each key and
+			// doing case-insensitive search.
+			for dataValKey := range dataValKeys {
+				mK, ok := dataValKey.Interface().(string)
+				if !ok {
+					// Not a string key
+					continue
+				}
+
+				if d.config.MatchName(mK, fieldName) {
+					rawMapKey = dataValKey
+					rawMapVal = dataVal.MapIndex(dataValKey)
+					break
+				}
+			}
+
+			if !rawMapVal.IsValid() {
+				// There was no matching key in the map for the value in
+				// the struct. Remember it for potential errors and metadata.
+				targetValKeysUnused[fieldName] = struct{}{}
+				continue
+			}
+		}
+
+		if !fieldValue.IsValid() {
+			// This should never happen
+			panic("field is not valid")
+		}
+
+		// If we can't set the field, then it is unexported or something,
+		// and we just continue onwards.
+		if !fieldValue.CanSet() {
+			continue
+		}
+
+		// Delete the key we're using from the unused map so we stop tracking
+		delete(dataValKeysUnused, rawMapKey.Interface())
+
+		// If the name is empty string, then we're at the root, and we
+		// don't dot-join the fields.
+		if name != "" {
+			fieldName = name + "." + fieldName
+		}
+
+		if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {
+			errors = appendErrors(errors, err)
+		}
+	}
+
+	// If we have a "remain"-tagged field and we have unused keys then
+	// we put the unused keys directly into the remain field.
+	if remainField != nil && len(dataValKeysUnused) > 0 {
+		// Build a map of only the unused values
+		remain := map[interface{}]interface{}{}
+		for key := range dataValKeysUnused {
+			remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()
+		}
+
+		// Decode it as-if we were just decoding this map onto our map.
+		if err := d.decodeMap(name, remain, remainField.val); err != nil {
+			errors = appendErrors(errors, err)
+		}
+
+		// Set the map to nil so we have none so that the next check will
+		// not error (ErrorUnused)
+		dataValKeysUnused = nil
+	}
+
+	if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {
+		keys := make([]string, 0, len(dataValKeysUnused))
+		for rawKey := range dataValKeysUnused {
+			keys = append(keys, rawKey.(string))
+		}
+		sort.Strings(keys)
+
+		err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
+		errors = appendErrors(errors, err)
+	}
+
+	if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
+		keys := make([]string, 0, len(targetValKeysUnused))
+		for rawKey := range targetValKeysUnused {
+			keys = append(keys, rawKey.(string))
+		}
+		sort.Strings(keys)
+
+		err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
+		errors = appendErrors(errors, err)
+	}
+
+	if len(errors) > 0 {
+		return &Error{errors}
+	}
+
+	// Add the unused keys to the list of unused keys if we're tracking metadata
+	if d.config.Metadata != nil {
+		for rawKey := range dataValKeysUnused {
+			key := rawKey.(string)
+			if name != "" {
+				key = name + "." + key
+			}
+
+			d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)
+		}
+		for rawKey := range targetValKeysUnused {
+			key := rawKey.(string)
+			if name != "" {
+				key = name + "." + key
+			}
+
+			d.config.Metadata.Unset = append(d.config.Metadata.Unset, key)
+		}
+	}
+
+	return nil
+}
+
+func isEmptyValue(v reflect.Value) bool {
+	switch getKind(v) {
+	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+		return v.Len() == 0
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+	return false
+}
+
+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 isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool {
+	for i := 0; i < typ.NumField(); i++ {
+		f := typ.Field(i)
+		if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
+			return true
+		}
+		if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
+			return true
+		}
+	}
+	return false
+}
+
+func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
+	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
+		return v
+	}
+	deref := v.Elem()
+	derefT := deref.Type()
+	if isStructTypeConvertibleToMap(derefT, true, tagName) {
+		return deref
+	}
+	return v
+}
diff --git a/app/utils/mapstructure/mapstructure_benchmark_test.go b/app/utils/mapstructure/mapstructure_benchmark_test.go
new file mode 100644
index 0000000..534caa2
--- /dev/null
+++ b/app/utils/mapstructure/mapstructure_benchmark_test.go
@@ -0,0 +1,285 @@
+package mapstructure
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+type Person struct {
+	Name   string
+	Age    int
+	Emails []string
+	Extra  map[string]string
+}
+
+func Benchmark_Decode(b *testing.B) {
+	input := map[string]interface{}{
+		"name":   "Mitchell",
+		"age":    91,
+		"emails": []string{"one", "two", "three"},
+		"extra": map[string]string{
+			"twitter": "mitchellh",
+		},
+	}
+
+	var result Person
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
+
+// decodeViaJSON takes the map data and passes it through encoding/json to convert it into the
+// given Go native structure pointed to by v. v must be a pointer to a struct.
+func decodeViaJSON(data interface{}, v interface{}) error {
+	// Perform the task by simply marshalling the input into JSON,
+	// then unmarshalling it into target native Go struct.
+	b, err := json.Marshal(data)
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(b, v)
+}
+
+func Benchmark_DecodeViaJSON(b *testing.B) {
+	input := map[string]interface{}{
+		"name":   "Mitchell",
+		"age":    91,
+		"emails": []string{"one", "two", "three"},
+		"extra": map[string]string{
+			"twitter": "mitchellh",
+		},
+	}
+
+	var result Person
+	for i := 0; i < b.N; i++ {
+		decodeViaJSON(input, &result)
+	}
+}
+
+func Benchmark_JSONUnmarshal(b *testing.B) {
+	input := map[string]interface{}{
+		"name":   "Mitchell",
+		"age":    91,
+		"emails": []string{"one", "two", "three"},
+		"extra": map[string]string{
+			"twitter": "mitchellh",
+		},
+	}
+
+	inputB, err := json.Marshal(input)
+	if err != nil {
+		b.Fatal("Failed to marshal test input:", err)
+	}
+
+	var result Person
+	for i := 0; i < b.N; i++ {
+		json.Unmarshal(inputB, &result)
+	}
+}
+
+func Benchmark_DecodeBasic(b *testing.B) {
+	input := map[string]interface{}{
+		"vstring":     "foo",
+		"vint":        42,
+		"Vuint":       42,
+		"vbool":       true,
+		"Vfloat":      42.42,
+		"vsilent":     true,
+		"vdata":       42,
+		"vjsonInt":    json.Number("1234"),
+		"vjsonFloat":  json.Number("1234.5"),
+		"vjsonNumber": json.Number("1234.5"),
+	}
+
+	for i := 0; i < b.N; i++ {
+		var result Basic
+		Decode(input, &result)
+	}
+}
+
+func Benchmark_DecodeEmbedded(b *testing.B) {
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"Basic": map[string]interface{}{
+			"vstring": "innerfoo",
+		},
+		"vunique": "bar",
+	}
+
+	var result Embedded
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
+
+func Benchmark_DecodeTypeConversion(b *testing.B) {
+	input := map[string]interface{}{
+		"IntToFloat":    42,
+		"IntToUint":     42,
+		"IntToBool":     1,
+		"IntToString":   42,
+		"UintToInt":     42,
+		"UintToFloat":   42,
+		"UintToBool":    42,
+		"UintToString":  42,
+		"BoolToInt":     true,
+		"BoolToUint":    true,
+		"BoolToFloat":   true,
+		"BoolToString":  true,
+		"FloatToInt":    42.42,
+		"FloatToUint":   42.42,
+		"FloatToBool":   42.42,
+		"FloatToString": 42.42,
+		"StringToInt":   "42",
+		"StringToUint":  "42",
+		"StringToBool":  "1",
+		"StringToFloat": "42.42",
+		"SliceToMap":    []interface{}{},
+		"MapToSlice":    map[string]interface{}{},
+	}
+
+	var resultStrict TypeConversionResult
+	for i := 0; i < b.N; i++ {
+		Decode(input, &resultStrict)
+	}
+}
+
+func Benchmark_DecodeMap(b *testing.B) {
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vother": map[interface{}]interface{}{
+			"foo": "foo",
+			"bar": "bar",
+		},
+	}
+
+	var result Map
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
+
+func Benchmark_DecodeMapOfStruct(b *testing.B) {
+	input := map[string]interface{}{
+		"value": map[string]interface{}{
+			"foo": map[string]string{"vstring": "one"},
+			"bar": map[string]string{"vstring": "two"},
+		},
+	}
+
+	var result MapOfStruct
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
+
+func Benchmark_DecodeSlice(b *testing.B) {
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": []string{"foo", "bar", "baz"},
+	}
+
+	var result Slice
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
+
+func Benchmark_DecodeSliceOfStruct(b *testing.B) {
+	input := map[string]interface{}{
+		"value": []map[string]interface{}{
+			{"vstring": "one"},
+			{"vstring": "two"},
+		},
+	}
+
+	var result SliceOfStruct
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
+
+func Benchmark_DecodeWeaklyTypedInput(b *testing.B) {
+	// This input can come from anywhere, but typically comes from
+	// something like decoding JSON, generated by a weakly typed language
+	// such as PHP.
+	input := map[string]interface{}{
+		"name":   123,                      // number => string
+		"age":    "42",                     // string => number
+		"emails": map[string]interface{}{}, // empty map => empty array
+	}
+
+	var result Person
+	config := &DecoderConfig{
+		WeaklyTypedInput: true,
+		Result:           &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		panic(err)
+	}
+
+	for i := 0; i < b.N; i++ {
+		decoder.Decode(input)
+	}
+}
+
+func Benchmark_DecodeMetadata(b *testing.B) {
+	input := map[string]interface{}{
+		"name":  "Mitchell",
+		"age":   91,
+		"email": "foo@bar.com",
+	}
+
+	var md Metadata
+	var result Person
+	config := &DecoderConfig{
+		Metadata: &md,
+		Result:   &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		panic(err)
+	}
+
+	for i := 0; i < b.N; i++ {
+		decoder.Decode(input)
+	}
+}
+
+func Benchmark_DecodeMetadataEmbedded(b *testing.B) {
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	var md Metadata
+	var result EmbeddedSquash
+	config := &DecoderConfig{
+		Metadata: &md,
+		Result:   &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		b.Fatalf("err: %s", err)
+	}
+
+	for i := 0; i < b.N; i++ {
+		decoder.Decode(input)
+	}
+}
+
+func Benchmark_DecodeTagged(b *testing.B) {
+	input := map[string]interface{}{
+		"foo": "bar",
+		"bar": "value",
+	}
+
+	var result Tagged
+	for i := 0; i < b.N; i++ {
+		Decode(input, &result)
+	}
+}
diff --git a/app/utils/mapstructure/mapstructure_bugs_test.go b/app/utils/mapstructure/mapstructure_bugs_test.go
new file mode 100644
index 0000000..77bb313
--- /dev/null
+++ b/app/utils/mapstructure/mapstructure_bugs_test.go
@@ -0,0 +1,627 @@
+package mapstructure
+
+import (
+	"reflect"
+	"testing"
+	"time"
+)
+
+// GH-1, GH-10, GH-96
+func TestDecode_NilValue(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name       string
+		in         interface{}
+		target     interface{}
+		out        interface{}
+		metaKeys   []string
+		metaUnused []string
+	}{
+		{
+			"all nil",
+			&map[string]interface{}{
+				"vfoo":   nil,
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "", Vother: nil},
+			[]string{"Vfoo", "Vother"},
+			[]string{},
+		},
+		{
+			"partial nil",
+			&map[string]interface{}{
+				"vfoo":   "baz",
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "baz", Vother: nil},
+			[]string{"Vfoo", "Vother"},
+			[]string{},
+		},
+		{
+			"partial decode",
+			&map[string]interface{}{
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "foo", Vother: nil},
+			[]string{"Vother"},
+			[]string{},
+		},
+		{
+			"unused values",
+			&map[string]interface{}{
+				"vbar":   "bar",
+				"vfoo":   nil,
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "", Vother: nil},
+			[]string{"Vfoo", "Vother"},
+			[]string{"vbar"},
+		},
+		{
+			"map interface all nil",
+			&map[interface{}]interface{}{
+				"vfoo":   nil,
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "", Vother: nil},
+			[]string{"Vfoo", "Vother"},
+			[]string{},
+		},
+		{
+			"map interface partial nil",
+			&map[interface{}]interface{}{
+				"vfoo":   "baz",
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "baz", Vother: nil},
+			[]string{"Vfoo", "Vother"},
+			[]string{},
+		},
+		{
+			"map interface partial decode",
+			&map[interface{}]interface{}{
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "foo", Vother: nil},
+			[]string{"Vother"},
+			[]string{},
+		},
+		{
+			"map interface unused values",
+			&map[interface{}]interface{}{
+				"vbar":   "bar",
+				"vfoo":   nil,
+				"vother": nil,
+			},
+			&Map{Vfoo: "foo", Vother: map[string]string{"foo": "bar"}},
+			&Map{Vfoo: "", Vother: nil},
+			[]string{"Vfoo", "Vother"},
+			[]string{"vbar"},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			config := &DecoderConfig{
+				Metadata:   new(Metadata),
+				Result:     tc.target,
+				ZeroFields: true,
+			}
+
+			decoder, err := NewDecoder(config)
+			if err != nil {
+				t.Fatalf("should not error: %s", err)
+			}
+
+			err = decoder.Decode(tc.in)
+			if err != nil {
+				t.Fatalf("should not error: %s", err)
+			}
+
+			if !reflect.DeepEqual(tc.out, tc.target) {
+				t.Fatalf("%q: TestDecode_NilValue() expected: %#v, got: %#v", tc.name, tc.out, tc.target)
+			}
+
+			if !reflect.DeepEqual(tc.metaKeys, config.Metadata.Keys) {
+				t.Fatalf("%q: Metadata.Keys mismatch expected: %#v, got: %#v", tc.name, tc.metaKeys, config.Metadata.Keys)
+			}
+
+			if !reflect.DeepEqual(tc.metaUnused, config.Metadata.Unused) {
+				t.Fatalf("%q: Metadata.Unused mismatch expected: %#v, got: %#v", tc.name, tc.metaUnused, config.Metadata.Unused)
+			}
+		})
+	}
+}
+
+// #48
+func TestNestedTypePointerWithDefaults(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": map[string]interface{}{
+			"vstring": "foo",
+			"vint":    42,
+			"vbool":   true,
+		},
+	}
+
+	result := NestedPointer{
+		Vbar: &Basic{
+			Vuint: 42,
+		},
+	}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vbar.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+	}
+
+	if result.Vbar.Vint != 42 {
+		t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+	}
+
+	if result.Vbar.Vbool != true {
+		t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+	}
+
+	if result.Vbar.Vextra != "" {
+		t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+	}
+
+	// this is the error
+	if result.Vbar.Vuint != 42 {
+		t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint)
+	}
+
+}
+
+type NestedSlice struct {
+	Vfoo   string
+	Vbars  []Basic
+	Vempty []Basic
+}
+
+// #48
+func TestNestedTypeSliceWithDefaults(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbars": []map[string]interface{}{
+			{"vstring": "foo", "vint": 42, "vbool": true},
+			{"vint": 42, "vbool": true},
+		},
+		"vempty": []map[string]interface{}{
+			{"vstring": "foo", "vint": 42, "vbool": true},
+			{"vint": 42, "vbool": true},
+		},
+	}
+
+	result := NestedSlice{
+		Vbars: []Basic{
+			{Vuint: 42},
+			{Vstring: "foo"},
+		},
+	}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vbars[0].Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vbars[0].Vstring)
+	}
+	// this is the error
+	if result.Vbars[0].Vuint != 42 {
+		t.Errorf("vuint value should be 42: %#v", result.Vbars[0].Vuint)
+	}
+}
+
+// #48 workaround
+func TestNestedTypeWithDefaults(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": map[string]interface{}{
+			"vstring": "foo",
+			"vint":    42,
+			"vbool":   true,
+		},
+	}
+
+	result := Nested{
+		Vbar: Basic{
+			Vuint: 42,
+		},
+	}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vbar.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+	}
+
+	if result.Vbar.Vint != 42 {
+		t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+	}
+
+	if result.Vbar.Vbool != true {
+		t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+	}
+
+	if result.Vbar.Vextra != "" {
+		t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+	}
+
+	// this is the error
+	if result.Vbar.Vuint != 42 {
+		t.Errorf("vuint value should be 42: %#v", result.Vbar.Vuint)
+	}
+
+}
+
+// #67 panic() on extending slices (decodeSlice with disabled ZeroValues)
+func TestDecodeSliceToEmptySliceWOZeroing(t *testing.T) {
+	t.Parallel()
+
+	type TestStruct struct {
+		Vfoo []string
+	}
+
+	decode := func(m interface{}, rawVal interface{}) error {
+		config := &DecoderConfig{
+			Metadata:   nil,
+			Result:     rawVal,
+			ZeroFields: false,
+		}
+
+		decoder, err := NewDecoder(config)
+		if err != nil {
+			return err
+		}
+
+		return decoder.Decode(m)
+	}
+
+	{
+		input := map[string]interface{}{
+			"vfoo": []string{"1"},
+		}
+
+		result := &TestStruct{}
+
+		err := decode(input, &result)
+		if err != nil {
+			t.Fatalf("got an err: %s", err.Error())
+		}
+	}
+
+	{
+		input := map[string]interface{}{
+			"vfoo": []string{"1"},
+		}
+
+		result := &TestStruct{
+			Vfoo: []string{},
+		}
+
+		err := decode(input, &result)
+		if err != nil {
+			t.Fatalf("got an err: %s", err.Error())
+		}
+	}
+
+	{
+		input := map[string]interface{}{
+			"vfoo": []string{"2", "3"},
+		}
+
+		result := &TestStruct{
+			Vfoo: []string{"1"},
+		}
+
+		err := decode(input, &result)
+		if err != nil {
+			t.Fatalf("got an err: %s", err.Error())
+		}
+	}
+}
+
+// #70
+func TestNextSquashMapstructure(t *testing.T) {
+	data := &struct {
+		Level1 struct {
+			Level2 struct {
+				Foo string
+			} `mapstructure:",squash"`
+		} `mapstructure:",squash"`
+	}{}
+	err := Decode(map[interface{}]interface{}{"foo": "baz"}, &data)
+	if err != nil {
+		t.Fatalf("should not error: %s", err)
+	}
+	if data.Level1.Level2.Foo != "baz" {
+		t.Fatal("value should be baz")
+	}
+}
+
+type ImplementsInterfacePointerReceiver struct {
+	Name string
+}
+
+func (i *ImplementsInterfacePointerReceiver) DoStuff() {}
+
+type ImplementsInterfaceValueReceiver string
+
+func (i ImplementsInterfaceValueReceiver) DoStuff() {}
+
+// GH-140 Type error when using DecodeHook to decode into interface
+func TestDecode_DecodeHookInterface(t *testing.T) {
+	t.Parallel()
+
+	type Interface interface {
+		DoStuff()
+	}
+	type DecodeIntoInterface struct {
+		Test Interface
+	}
+
+	testData := map[string]string{"test": "test"}
+
+	stringToPointerInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
+		if from.Kind() != reflect.String {
+			return data, nil
+		}
+
+		if to != reflect.TypeOf((*Interface)(nil)).Elem() {
+			return data, nil
+		}
+		// Ensure interface is satisfied
+		var impl Interface = &ImplementsInterfacePointerReceiver{data.(string)}
+		return impl, nil
+	}
+
+	stringToValueInterfaceDecodeHook := func(from, to reflect.Type, data interface{}) (interface{}, error) {
+		if from.Kind() != reflect.String {
+			return data, nil
+		}
+
+		if to != reflect.TypeOf((*Interface)(nil)).Elem() {
+			return data, nil
+		}
+		// Ensure interface is satisfied
+		var impl Interface = ImplementsInterfaceValueReceiver(data.(string))
+		return impl, nil
+	}
+
+	{
+		decodeInto := new(DecodeIntoInterface)
+
+		decoder, _ := NewDecoder(&DecoderConfig{
+			DecodeHook: stringToPointerInterfaceDecodeHook,
+			Result:     decodeInto,
+		})
+
+		err := decoder.Decode(testData)
+		if err != nil {
+			t.Fatalf("Decode returned error: %s", err)
+		}
+
+		expected := &ImplementsInterfacePointerReceiver{"test"}
+		if !reflect.DeepEqual(decodeInto.Test, expected) {
+			t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
+		}
+	}
+
+	{
+		decodeInto := new(DecodeIntoInterface)
+
+		decoder, _ := NewDecoder(&DecoderConfig{
+			DecodeHook: stringToValueInterfaceDecodeHook,
+			Result:     decodeInto,
+		})
+
+		err := decoder.Decode(testData)
+		if err != nil {
+			t.Fatalf("Decode returned error: %s", err)
+		}
+
+		expected := ImplementsInterfaceValueReceiver("test")
+		if !reflect.DeepEqual(decodeInto.Test, expected) {
+			t.Fatalf("expected: %#v (%T), got: %#v (%T)", decodeInto.Test, decodeInto.Test, expected, expected)
+		}
+	}
+}
+
+// #103 Check for data type before trying to access its composants prevent a panic error
+// in decodeSlice
+func TestDecodeBadDataTypeInSlice(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"Toto": "titi",
+	}
+	result := []struct {
+		Toto string
+	}{}
+
+	if err := Decode(input, &result); err == nil {
+		t.Error("An error was expected, got nil")
+	}
+}
+
+// #202 Ensure that intermediate maps in the struct -> struct decode process are settable
+// and not just the elements within them.
+func TestDecodeIntermediateMapsSettable(t *testing.T) {
+	type Timestamp struct {
+		Seconds int64
+		Nanos   int32
+	}
+
+	type TsWrapper struct {
+		Timestamp *Timestamp
+	}
+
+	type TimeWrapper struct {
+		Timestamp time.Time
+	}
+
+	input := TimeWrapper{
+		Timestamp: time.Unix(123456789, 987654),
+	}
+
+	expected := TsWrapper{
+		Timestamp: &Timestamp{
+			Seconds: 123456789,
+			Nanos:   987654,
+		},
+	}
+
+	timePtrType := reflect.TypeOf((*time.Time)(nil))
+	mapStrInfType := reflect.TypeOf((map[string]interface{})(nil))
+
+	var actual TsWrapper
+	decoder, err := NewDecoder(&DecoderConfig{
+		Result: &actual,
+		DecodeHook: func(from, to reflect.Type, data interface{}) (interface{}, error) {
+			if from == timePtrType && to == mapStrInfType {
+				ts := data.(*time.Time)
+				nanos := ts.UnixNano()
+
+				seconds := nanos / 1000000000
+				nanos = nanos % 1000000000
+
+				return &map[string]interface{}{
+					"Seconds": seconds,
+					"Nanos":   int32(nanos),
+				}, nil
+			}
+			return data, nil
+		},
+	})
+
+	if err != nil {
+		t.Fatalf("failed to create decoder: %v", err)
+	}
+
+	if err := decoder.Decode(&input); err != nil {
+		t.Fatalf("failed to decode input: %v", err)
+	}
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Fatalf("expected: %#[1]v (%[1]T), got: %#[2]v (%[2]T)", expected, actual)
+	}
+}
+
+// GH-206: decodeInt throws an error for an empty string
+func TestDecode_weakEmptyStringToInt(t *testing.T) {
+	input := map[string]interface{}{
+		"StringToInt":   "",
+		"StringToUint":  "",
+		"StringToBool":  "",
+		"StringToFloat": "",
+	}
+
+	expectedResultWeak := TypeConversionResult{
+		StringToInt:   0,
+		StringToUint:  0,
+		StringToBool:  false,
+		StringToFloat: 0,
+	}
+
+	// Test weak type conversion
+	var resultWeak TypeConversionResult
+	err := WeakDecode(input, &resultWeak)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if !reflect.DeepEqual(resultWeak, expectedResultWeak) {
+		t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak)
+	}
+}
+
+// GH-228: Squash cause *time.Time set to zero
+func TestMapSquash(t *testing.T) {
+	type AA struct {
+		T *time.Time
+	}
+	type A struct {
+		AA
+	}
+
+	v := time.Now()
+	in := &AA{
+		T: &v,
+	}
+	out := &A{}
+	d, err := NewDecoder(&DecoderConfig{
+		Squash: true,
+		Result: out,
+	})
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+	if err := d.Decode(in); err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	// these failed
+	if !v.Equal(*out.T) {
+		t.Fatal("expected equal")
+	}
+	if out.T.IsZero() {
+		t.Fatal("expected false")
+	}
+}
+
+// GH-238: Empty key name when decoding map from struct with only omitempty flag
+func TestMapOmitEmptyWithEmptyFieldnameInTag(t *testing.T) {
+	type Struct struct {
+		Username string `mapstructure:",omitempty"`
+		Age      int    `mapstructure:",omitempty"`
+	}
+
+	s := Struct{
+		Username: "Joe",
+	}
+	var m map[string]interface{}
+
+	if err := Decode(s, &m); err != nil {
+		t.Fatal(err)
+	}
+
+	if len(m) != 1 {
+		t.Fatalf("fail: %#v", m)
+	}
+	if m["Username"] != "Joe" {
+		t.Fatalf("fail: %#v", m)
+	}
+}
diff --git a/app/utils/mapstructure/mapstructure_examples_test.go b/app/utils/mapstructure/mapstructure_examples_test.go
new file mode 100644
index 0000000..2413b69
--- /dev/null
+++ b/app/utils/mapstructure/mapstructure_examples_test.go
@@ -0,0 +1,256 @@
+package mapstructure
+
+import (
+	"fmt"
+)
+
+func ExampleDecode() {
+	type Person struct {
+		Name   string
+		Age    int
+		Emails []string
+		Extra  map[string]string
+	}
+
+	// This input can come from anywhere, but typically comes from
+	// something like decoding JSON where we're not quite sure of the
+	// struct initially.
+	input := map[string]interface{}{
+		"name":   "Mitchell",
+		"age":    91,
+		"emails": []string{"one", "two", "three"},
+		"extra": map[string]string{
+			"twitter": "mitchellh",
+		},
+	}
+
+	var result Person
+	err := Decode(input, &result)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("%#v", result)
+	// Output:
+	// mapstructure.Person{Name:"Mitchell", Age:91, Emails:[]string{"one", "two", "three"}, Extra:map[string]string{"twitter":"mitchellh"}}
+}
+
+func ExampleDecode_errors() {
+	type Person struct {
+		Name   string
+		Age    int
+		Emails []string
+		Extra  map[string]string
+	}
+
+	// This input can come from anywhere, but typically comes from
+	// something like decoding JSON where we're not quite sure of the
+	// struct initially.
+	input := map[string]interface{}{
+		"name":   123,
+		"age":    "bad value",
+		"emails": []int{1, 2, 3},
+	}
+
+	var result Person
+	err := Decode(input, &result)
+	if err == nil {
+		panic("should have an error")
+	}
+
+	fmt.Println(err.Error())
+	// Output:
+	// 5 error(s) decoding:
+	//
+	// * 'Age' expected type 'int', got unconvertible type 'string', value: 'bad value'
+	// * 'Emails[0]' expected type 'string', got unconvertible type 'int', value: '1'
+	// * 'Emails[1]' expected type 'string', got unconvertible type 'int', value: '2'
+	// * 'Emails[2]' expected type 'string', got unconvertible type 'int', value: '3'
+	// * 'Name' expected type 'string', got unconvertible type 'int', value: '123'
+}
+
+func ExampleDecode_metadata() {
+	type Person struct {
+		Name string
+		Age  int
+	}
+
+	// This input can come from anywhere, but typically comes from
+	// something like decoding JSON where we're not quite sure of the
+	// struct initially.
+	input := map[string]interface{}{
+		"name":  "Mitchell",
+		"age":   91,
+		"email": "foo@bar.com",
+	}
+
+	// For metadata, we make a more advanced DecoderConfig so we can
+	// more finely configure the decoder that is used. In this case, we
+	// just tell the decoder we want to track metadata.
+	var md Metadata
+	var result Person
+	config := &DecoderConfig{
+		Metadata: &md,
+		Result:   &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		panic(err)
+	}
+
+	if err := decoder.Decode(input); err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("Unused keys: %#v", md.Unused)
+	// Output:
+	// Unused keys: []string{"email"}
+}
+
+func ExampleDecode_weaklyTypedInput() {
+	type Person struct {
+		Name   string
+		Age    int
+		Emails []string
+	}
+
+	// This input can come from anywhere, but typically comes from
+	// something like decoding JSON, generated by a weakly typed language
+	// such as PHP.
+	input := map[string]interface{}{
+		"name":   123,                      // number => string
+		"age":    "42",                     // string => number
+		"emails": map[string]interface{}{}, // empty map => empty array
+	}
+
+	var result Person
+	config := &DecoderConfig{
+		WeaklyTypedInput: true,
+		Result:           &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		panic(err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("%#v", result)
+	// Output: mapstructure.Person{Name:"123", Age:42, Emails:[]string{}}
+}
+
+func ExampleDecode_tags() {
+	// Note that the mapstructure tags defined in the struct type
+	// can indicate which fields the values are mapped to.
+	type Person struct {
+		Name string `mapstructure:"person_name"`
+		Age  int    `mapstructure:"person_age"`
+	}
+
+	input := map[string]interface{}{
+		"person_name": "Mitchell",
+		"person_age":  91,
+	}
+
+	var result Person
+	err := Decode(input, &result)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("%#v", result)
+	// Output:
+	// mapstructure.Person{Name:"Mitchell", Age:91}
+}
+
+func ExampleDecode_embeddedStruct() {
+	// Squashing multiple embedded structs is allowed using the squash tag.
+	// This is demonstrated by creating a composite struct of multiple types
+	// and decoding into it. In this case, a person can carry with it both
+	// a Family and a Location, as well as their own FirstName.
+	type Family struct {
+		LastName string
+	}
+	type Location struct {
+		City string
+	}
+	type Person struct {
+		Family    `mapstructure:",squash"`
+		Location  `mapstructure:",squash"`
+		FirstName string
+	}
+
+	input := map[string]interface{}{
+		"FirstName": "Mitchell",
+		"LastName":  "Hashimoto",
+		"City":      "San Francisco",
+	}
+
+	var result Person
+	err := Decode(input, &result)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("%s %s, %s", result.FirstName, result.LastName, result.City)
+	// Output:
+	// Mitchell Hashimoto, San Francisco
+}
+
+func ExampleDecode_remainingData() {
+	// Note that the mapstructure tags defined in the struct type
+	// can indicate which fields the values are mapped to.
+	type Person struct {
+		Name  string
+		Age   int
+		Other map[string]interface{} `mapstructure:",remain"`
+	}
+
+	input := map[string]interface{}{
+		"name":  "Mitchell",
+		"age":   91,
+		"email": "mitchell@example.com",
+	}
+
+	var result Person
+	err := Decode(input, &result)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("%#v", result)
+	// Output:
+	// mapstructure.Person{Name:"Mitchell", Age:91, Other:map[string]interface {}{"email":"mitchell@example.com"}}
+}
+
+func ExampleDecode_omitempty() {
+	// Add omitempty annotation to avoid map keys for empty values
+	type Family struct {
+		LastName string
+	}
+	type Location struct {
+		City string
+	}
+	type Person struct {
+		*Family   `mapstructure:",omitempty"`
+		*Location `mapstructure:",omitempty"`
+		Age       int
+		FirstName string
+	}
+
+	result := &map[string]interface{}{}
+	input := Person{FirstName: "Somebody"}
+	err := Decode(input, &result)
+	if err != nil {
+		panic(err)
+	}
+
+	fmt.Printf("%+v", result)
+	// Output:
+	// &map[Age:0 FirstName:Somebody]
+}
diff --git a/app/utils/mapstructure/mapstructure_ext_test.go b/app/utils/mapstructure/mapstructure_ext_test.go
new file mode 100644
index 0000000..bd5f808
--- /dev/null
+++ b/app/utils/mapstructure/mapstructure_ext_test.go
@@ -0,0 +1,58 @@
+package mapstructure
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestDecode_Ptr(t *testing.T) {
+	t.Parallel()
+
+	type G struct {
+		Id   int
+		Name string
+	}
+
+	type X struct {
+		Id   int
+		Name int
+	}
+
+	type AG struct {
+		List []*G
+	}
+
+	type AX struct {
+		List []*X
+	}
+
+	g2 := &AG{
+		List: []*G{
+			{
+				Id:   11,
+				Name: "gg",
+			},
+		},
+	}
+	x2 := AX{}
+
+	// 报错但还是会转换成功,转换后值为目标类型的 0 值
+	err := Decode(g2, &x2)
+
+	res := AX{
+		List: []*X{
+			{
+				Id:   11,
+				Name: 0, // 这个类型的 0 值
+			},
+		},
+	}
+
+	if err == nil {
+		t.Errorf("Decode_Ptr err should not be 'nil': %#v", err)
+	}
+
+	if !reflect.DeepEqual(res, x2) {
+		t.Errorf("result should be %#v: got %#v", res, x2)
+	}
+}
diff --git a/app/utils/mapstructure/mapstructure_test.go b/app/utils/mapstructure/mapstructure_test.go
new file mode 100644
index 0000000..bef5f85
--- /dev/null
+++ b/app/utils/mapstructure/mapstructure_test.go
@@ -0,0 +1,2763 @@
+package mapstructure
+
+import (
+	"encoding/json"
+	"io"
+	"reflect"
+	"sort"
+	"strings"
+	"testing"
+	"time"
+)
+
+type Basic struct {
+	Vstring     string
+	Vint        int
+	Vint8       int8
+	Vint16      int16
+	Vint32      int32
+	Vint64      int64
+	Vuint       uint
+	Vbool       bool
+	Vfloat      float64
+	Vextra      string
+	vsilent     bool
+	Vdata       interface{}
+	VjsonInt    int
+	VjsonUint   uint
+	VjsonUint64 uint64
+	VjsonFloat  float64
+	VjsonNumber json.Number
+}
+
+type BasicPointer struct {
+	Vstring     *string
+	Vint        *int
+	Vuint       *uint
+	Vbool       *bool
+	Vfloat      *float64
+	Vextra      *string
+	vsilent     *bool
+	Vdata       *interface{}
+	VjsonInt    *int
+	VjsonFloat  *float64
+	VjsonNumber *json.Number
+}
+
+type BasicSquash struct {
+	Test Basic `mapstructure:",squash"`
+}
+
+type Embedded struct {
+	Basic
+	Vunique string
+}
+
+type EmbeddedPointer struct {
+	*Basic
+	Vunique string
+}
+
+type EmbeddedSquash struct {
+	Basic   `mapstructure:",squash"`
+	Vunique string
+}
+
+type EmbeddedPointerSquash struct {
+	*Basic  `mapstructure:",squash"`
+	Vunique string
+}
+
+type BasicMapStructure struct {
+	Vunique string     `mapstructure:"vunique"`
+	Vtime   *time.Time `mapstructure:"time"`
+}
+
+type NestedPointerWithMapstructure struct {
+	Vbar *BasicMapStructure `mapstructure:"vbar"`
+}
+
+type EmbeddedPointerSquashWithNestedMapstructure struct {
+	*NestedPointerWithMapstructure `mapstructure:",squash"`
+	Vunique                        string
+}
+
+type EmbeddedAndNamed struct {
+	Basic
+	Named   Basic
+	Vunique string
+}
+
+type SliceAlias []string
+
+type EmbeddedSlice struct {
+	SliceAlias `mapstructure:"slice_alias"`
+	Vunique    string
+}
+
+type ArrayAlias [2]string
+
+type EmbeddedArray struct {
+	ArrayAlias `mapstructure:"array_alias"`
+	Vunique    string
+}
+
+type SquashOnNonStructType struct {
+	InvalidSquashType int `mapstructure:",squash"`
+}
+
+type Map struct {
+	Vfoo   string
+	Vother map[string]string
+}
+
+type MapOfStruct struct {
+	Value map[string]Basic
+}
+
+type Nested struct {
+	Vfoo string
+	Vbar Basic
+}
+
+type NestedPointer struct {
+	Vfoo string
+	Vbar *Basic
+}
+
+type NilInterface struct {
+	W io.Writer
+}
+
+type NilPointer struct {
+	Value *string
+}
+
+type Slice struct {
+	Vfoo string
+	Vbar []string
+}
+
+type SliceOfAlias struct {
+	Vfoo string
+	Vbar SliceAlias
+}
+
+type SliceOfStruct struct {
+	Value []Basic
+}
+
+type SlicePointer struct {
+	Vbar *[]string
+}
+
+type Array struct {
+	Vfoo string
+	Vbar [2]string
+}
+
+type ArrayOfStruct struct {
+	Value [2]Basic
+}
+
+type Func struct {
+	Foo func() string
+}
+
+type Tagged struct {
+	Extra string `mapstructure:"bar,what,what"`
+	Value string `mapstructure:"foo"`
+}
+
+type Remainder struct {
+	A     string
+	Extra map[string]interface{} `mapstructure:",remain"`
+}
+
+type StructWithOmitEmpty struct {
+	VisibleStringField string                 `mapstructure:"visible-string"`
+	OmitStringField    string                 `mapstructure:"omittable-string,omitempty"`
+	VisibleIntField    int                    `mapstructure:"visible-int"`
+	OmitIntField       int                    `mapstructure:"omittable-int,omitempty"`
+	VisibleFloatField  float64                `mapstructure:"visible-float"`
+	OmitFloatField     float64                `mapstructure:"omittable-float,omitempty"`
+	VisibleSliceField  []interface{}          `mapstructure:"visible-slice"`
+	OmitSliceField     []interface{}          `mapstructure:"omittable-slice,omitempty"`
+	VisibleMapField    map[string]interface{} `mapstructure:"visible-map"`
+	OmitMapField       map[string]interface{} `mapstructure:"omittable-map,omitempty"`
+	NestedField        *Nested                `mapstructure:"visible-nested"`
+	OmitNestedField    *Nested                `mapstructure:"omittable-nested,omitempty"`
+}
+
+type TypeConversionResult struct {
+	IntToFloat         float32
+	IntToUint          uint
+	IntToBool          bool
+	IntToString        string
+	UintToInt          int
+	UintToFloat        float32
+	UintToBool         bool
+	UintToString       string
+	BoolToInt          int
+	BoolToUint         uint
+	BoolToFloat        float32
+	BoolToString       string
+	FloatToInt         int
+	FloatToUint        uint
+	FloatToBool        bool
+	FloatToString      string
+	SliceUint8ToString string
+	StringToSliceUint8 []byte
+	ArrayUint8ToString string
+	StringToInt        int
+	StringToUint       uint
+	StringToBool       bool
+	StringToFloat      float32
+	StringToStrSlice   []string
+	StringToIntSlice   []int
+	StringToStrArray   [1]string
+	StringToIntArray   [1]int
+	SliceToMap         map[string]interface{}
+	MapToSlice         []interface{}
+	ArrayToMap         map[string]interface{}
+	MapToArray         [1]interface{}
+}
+
+func TestBasicTypes(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring":     "foo",
+		"vint":        42,
+		"vint8":       42,
+		"vint16":      42,
+		"vint32":      42,
+		"vint64":      42,
+		"Vuint":       42,
+		"vbool":       true,
+		"Vfloat":      42.42,
+		"vsilent":     true,
+		"vdata":       42,
+		"vjsonInt":    json.Number("1234"),
+		"vjsonUint":   json.Number("1234"),
+		"vjsonUint64": json.Number("9223372036854775809"), // 2^63 + 1
+		"vjsonFloat":  json.Number("1234.5"),
+		"vjsonNumber": json.Number("1234.5"),
+	}
+
+	var result Basic
+	err := Decode(input, &result)
+	if err != nil {
+		t.Errorf("got an err: %s", err.Error())
+		t.FailNow()
+	}
+
+	if result.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+	}
+
+	if result.Vint != 42 {
+		t.Errorf("vint value should be 42: %#v", result.Vint)
+	}
+	if result.Vint8 != 42 {
+		t.Errorf("vint8 value should be 42: %#v", result.Vint)
+	}
+	if result.Vint16 != 42 {
+		t.Errorf("vint16 value should be 42: %#v", result.Vint)
+	}
+	if result.Vint32 != 42 {
+		t.Errorf("vint32 value should be 42: %#v", result.Vint)
+	}
+	if result.Vint64 != 42 {
+		t.Errorf("vint64 value should be 42: %#v", result.Vint)
+	}
+
+	if result.Vuint != 42 {
+		t.Errorf("vuint value should be 42: %#v", result.Vuint)
+	}
+
+	if result.Vbool != true {
+		t.Errorf("vbool value should be true: %#v", result.Vbool)
+	}
+
+	if result.Vfloat != 42.42 {
+		t.Errorf("vfloat value should be 42.42: %#v", result.Vfloat)
+	}
+
+	if result.Vextra != "" {
+		t.Errorf("vextra value should be empty: %#v", result.Vextra)
+	}
+
+	if result.vsilent != false {
+		t.Error("vsilent should not be set, it is unexported")
+	}
+
+	if result.Vdata != 42 {
+		t.Error("vdata should be valid")
+	}
+
+	if result.VjsonInt != 1234 {
+		t.Errorf("vjsonint value should be 1234: %#v", result.VjsonInt)
+	}
+
+	if result.VjsonUint != 1234 {
+		t.Errorf("vjsonuint value should be 1234: %#v", result.VjsonUint)
+	}
+
+	if result.VjsonUint64 != 9223372036854775809 {
+		t.Errorf("vjsonuint64 value should be 9223372036854775809: %#v", result.VjsonUint64)
+	}
+
+	if result.VjsonFloat != 1234.5 {
+		t.Errorf("vjsonfloat value should be 1234.5: %#v", result.VjsonFloat)
+	}
+
+	if !reflect.DeepEqual(result.VjsonNumber, json.Number("1234.5")) {
+		t.Errorf("vjsonnumber value should be '1234.5': %T, %#v", result.VjsonNumber, result.VjsonNumber)
+	}
+}
+
+func TestBasic_IntWithFloat(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vint": float64(42),
+	}
+
+	var result Basic
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+}
+
+func TestBasic_Merge(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vint": 42,
+	}
+
+	var result Basic
+	result.Vuint = 100
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	expected := Basic{
+		Vint:  42,
+		Vuint: 100,
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+// Test for issue #46.
+func TestBasic_Struct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vdata": map[string]interface{}{
+			"vstring": "foo",
+		},
+	}
+
+	var result, inner Basic
+	result.Vdata = &inner
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+	expected := Basic{
+		Vdata: &Basic{
+			Vstring: "foo",
+		},
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+func TestBasic_interfaceStruct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+	}
+
+	var iface interface{} = &Basic{}
+	err := Decode(input, &iface)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	expected := &Basic{
+		Vstring: "foo",
+	}
+	if !reflect.DeepEqual(iface, expected) {
+		t.Fatalf("bad: %#v", iface)
+	}
+}
+
+// Issue 187
+func TestBasic_interfaceStructNonPtr(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+	}
+
+	var iface interface{} = Basic{}
+	err := Decode(input, &iface)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	expected := Basic{
+		Vstring: "foo",
+	}
+	if !reflect.DeepEqual(iface, expected) {
+		t.Fatalf("bad: %#v", iface)
+	}
+}
+
+func TestDecode_BasicSquash(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+	}
+
+	var result BasicSquash
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Test.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Test.Vstring)
+	}
+}
+
+func TestDecodeFrom_BasicSquash(t *testing.T) {
+	t.Parallel()
+
+	var v interface{}
+	var ok bool
+
+	input := BasicSquash{
+		Test: Basic{
+			Vstring: "foo",
+		},
+	}
+
+	var result map[string]interface{}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if _, ok = result["Test"]; ok {
+		t.Error("test should not be present in map")
+	}
+
+	v, ok = result["Vstring"]
+	if !ok {
+		t.Error("vstring should be present in map")
+	} else if !reflect.DeepEqual(v, "foo") {
+		t.Errorf("vstring value should be 'foo': %#v", v)
+	}
+}
+
+func TestDecode_Embedded(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"Basic": map[string]interface{}{
+			"vstring": "innerfoo",
+		},
+		"vunique": "bar",
+	}
+
+	var result Embedded
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vstring != "innerfoo" {
+		t.Errorf("vstring value should be 'innerfoo': %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_EmbeddedPointer(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"Basic": map[string]interface{}{
+			"vstring": "innerfoo",
+		},
+		"vunique": "bar",
+	}
+
+	var result EmbeddedPointer
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	expected := EmbeddedPointer{
+		Basic: &Basic{
+			Vstring: "innerfoo",
+		},
+		Vunique: "bar",
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+func TestDecode_EmbeddedSlice(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"slice_alias": []string{"foo", "bar"},
+		"vunique":     "bar",
+	}
+
+	var result EmbeddedSlice
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if !reflect.DeepEqual(result.SliceAlias, SliceAlias([]string{"foo", "bar"})) {
+		t.Errorf("slice value: %#v", result.SliceAlias)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_EmbeddedArray(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"array_alias": [2]string{"foo", "bar"},
+		"vunique":     "bar",
+	}
+
+	var result EmbeddedArray
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if !reflect.DeepEqual(result.ArrayAlias, ArrayAlias([2]string{"foo", "bar"})) {
+		t.Errorf("array value: %#v", result.ArrayAlias)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_decodeSliceWithArray(t *testing.T) {
+	t.Parallel()
+
+	var result []int
+	input := [1]int{1}
+	expected := []int{1}
+	if err := Decode(input, &result); err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if !reflect.DeepEqual(expected, result) {
+		t.Errorf("wanted %+v, got %+v", expected, result)
+	}
+}
+
+func TestDecode_EmbeddedNoSquash(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	var result Embedded
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vstring != "" {
+		t.Errorf("vstring value should be empty: %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_EmbeddedPointerNoSquash(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	result := EmbeddedPointer{
+		Basic: &Basic{},
+	}
+
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	if result.Vstring != "" {
+		t.Errorf("vstring value should be empty: %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_EmbeddedSquash(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	var result EmbeddedSquash
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecodeFrom_EmbeddedSquash(t *testing.T) {
+	t.Parallel()
+
+	var v interface{}
+	var ok bool
+
+	input := EmbeddedSquash{
+		Basic: Basic{
+			Vstring: "foo",
+		},
+		Vunique: "bar",
+	}
+
+	var result map[string]interface{}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if _, ok = result["Basic"]; ok {
+		t.Error("basic should not be present in map")
+	}
+
+	v, ok = result["Vstring"]
+	if !ok {
+		t.Error("vstring should be present in map")
+	} else if !reflect.DeepEqual(v, "foo") {
+		t.Errorf("vstring value should be 'foo': %#v", v)
+	}
+
+	v, ok = result["Vunique"]
+	if !ok {
+		t.Error("vunique should be present in map")
+	} else if !reflect.DeepEqual(v, "bar") {
+		t.Errorf("vunique value should be 'bar': %#v", v)
+	}
+}
+
+func TestDecode_EmbeddedPointerSquash_FromStructToMap(t *testing.T) {
+	t.Parallel()
+
+	input := EmbeddedPointerSquash{
+		Basic: &Basic{
+			Vstring: "foo",
+		},
+		Vunique: "bar",
+	}
+
+	var result map[string]interface{}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result["Vstring"] != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result["Vstring"])
+	}
+
+	if result["Vunique"] != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result["Vunique"])
+	}
+}
+
+func TestDecode_EmbeddedPointerSquash_FromMapToStruct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"Vstring": "foo",
+		"Vunique": "bar",
+	}
+
+	result := EmbeddedPointerSquash{
+		Basic: &Basic{},
+	}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+}
+
+func TestDecode_EmbeddedPointerSquashWithNestedMapstructure_FromStructToMap(t *testing.T) {
+	t.Parallel()
+
+	vTime := time.Now()
+
+	input := EmbeddedPointerSquashWithNestedMapstructure{
+		NestedPointerWithMapstructure: &NestedPointerWithMapstructure{
+			Vbar: &BasicMapStructure{
+				Vunique: "bar",
+				Vtime:   &vTime,
+			},
+		},
+		Vunique: "foo",
+	}
+
+	var result map[string]interface{}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+	expected := map[string]interface{}{
+		"vbar": map[string]interface{}{
+			"vunique": "bar",
+			"time":    &vTime,
+		},
+		"Vunique": "foo",
+	}
+
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("result should be %#v: got %#v", expected, result)
+	}
+}
+
+func TestDecode_EmbeddedPointerSquashWithNestedMapstructure_FromMapToStruct(t *testing.T) {
+	t.Parallel()
+
+	vTime := time.Now()
+
+	input := map[string]interface{}{
+		"vbar": map[string]interface{}{
+			"vunique": "bar",
+			"time":    &vTime,
+		},
+		"Vunique": "foo",
+	}
+
+	result := EmbeddedPointerSquashWithNestedMapstructure{
+		NestedPointerWithMapstructure: &NestedPointerWithMapstructure{},
+	}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+	expected := EmbeddedPointerSquashWithNestedMapstructure{
+		NestedPointerWithMapstructure: &NestedPointerWithMapstructure{
+			Vbar: &BasicMapStructure{
+				Vunique: "bar",
+				Vtime:   &vTime,
+			},
+		},
+		Vunique: "foo",
+	}
+
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("result should be %#v: got %#v", expected, result)
+	}
+}
+
+func TestDecode_EmbeddedSquashConfig(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+		"Named": map[string]interface{}{
+			"vstring": "baz",
+		},
+	}
+
+	var result EmbeddedAndNamed
+	config := &DecoderConfig{
+		Squash: true,
+		Result: &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vstring)
+	}
+
+	if result.Vunique != "bar" {
+		t.Errorf("vunique value should be 'bar': %#v", result.Vunique)
+	}
+
+	if result.Named.Vstring != "baz" {
+		t.Errorf("Named.vstring value should be 'baz': %#v", result.Named.Vstring)
+	}
+}
+
+func TestDecodeFrom_EmbeddedSquashConfig(t *testing.T) {
+	t.Parallel()
+
+	input := EmbeddedAndNamed{
+		Basic:   Basic{Vstring: "foo"},
+		Named:   Basic{Vstring: "baz"},
+		Vunique: "bar",
+	}
+
+	result := map[string]interface{}{}
+	config := &DecoderConfig{
+		Squash: true,
+		Result: &result,
+	}
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if _, ok := result["Basic"]; ok {
+		t.Error("basic should not be present in map")
+	}
+
+	v, ok := result["Vstring"]
+	if !ok {
+		t.Error("vstring should be present in map")
+	} else if !reflect.DeepEqual(v, "foo") {
+		t.Errorf("vstring value should be 'foo': %#v", v)
+	}
+
+	v, ok = result["Vunique"]
+	if !ok {
+		t.Error("vunique should be present in map")
+	} else if !reflect.DeepEqual(v, "bar") {
+		t.Errorf("vunique value should be 'bar': %#v", v)
+	}
+
+	v, ok = result["Named"]
+	if !ok {
+		t.Error("Named should be present in map")
+	} else {
+		named := v.(map[string]interface{})
+		v, ok := named["Vstring"]
+		if !ok {
+			t.Error("Named: vstring should be present in map")
+		} else if !reflect.DeepEqual(v, "baz") {
+			t.Errorf("Named: vstring should be 'baz': %#v", v)
+		}
+	}
+}
+
+func TestDecodeFrom_EmbeddedSquashConfig_WithTags(t *testing.T) {
+	t.Parallel()
+
+	var v interface{}
+	var ok bool
+
+	input := EmbeddedSquash{
+		Basic: Basic{
+			Vstring: "foo",
+		},
+		Vunique: "bar",
+	}
+
+	result := map[string]interface{}{}
+	config := &DecoderConfig{
+		Squash: true,
+		Result: &result,
+	}
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if _, ok = result["Basic"]; ok {
+		t.Error("basic should not be present in map")
+	}
+
+	v, ok = result["Vstring"]
+	if !ok {
+		t.Error("vstring should be present in map")
+	} else if !reflect.DeepEqual(v, "foo") {
+		t.Errorf("vstring value should be 'foo': %#v", v)
+	}
+
+	v, ok = result["Vunique"]
+	if !ok {
+		t.Error("vunique should be present in map")
+	} else if !reflect.DeepEqual(v, "bar") {
+		t.Errorf("vunique value should be 'bar': %#v", v)
+	}
+}
+
+func TestDecode_SquashOnNonStructType(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"InvalidSquashType": 42,
+	}
+
+	var result SquashOnNonStructType
+	err := Decode(input, &result)
+	if err == nil {
+		t.Fatal("unexpected success decoding invalid squash field type")
+	} else if !strings.Contains(err.Error(), "unsupported type for squash") {
+		t.Fatalf("unexpected error message for invalid squash field type: %s", err)
+	}
+}
+
+func TestDecode_DecodeHook(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vint": "WHAT",
+	}
+
+	decodeHook := func(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) {
+		if from == reflect.String && to != reflect.String {
+			return 5, nil
+		}
+
+		return v, nil
+	}
+
+	var result Basic
+	config := &DecoderConfig{
+		DecodeHook: decodeHook,
+		Result:     &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.Vint != 5 {
+		t.Errorf("vint should be 5: %#v", result.Vint)
+	}
+}
+
+func TestDecode_DecodeHookType(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vint": "WHAT",
+	}
+
+	decodeHook := func(from reflect.Type, to reflect.Type, v interface{}) (interface{}, error) {
+		if from.Kind() == reflect.String &&
+			to.Kind() != reflect.String {
+			return 5, nil
+		}
+
+		return v, nil
+	}
+
+	var result Basic
+	config := &DecoderConfig{
+		DecodeHook: decodeHook,
+		Result:     &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.Vint != 5 {
+		t.Errorf("vint should be 5: %#v", result.Vint)
+	}
+}
+
+func TestDecode_Nil(t *testing.T) {
+	t.Parallel()
+
+	var input interface{}
+	result := Basic{
+		Vstring: "foo",
+	}
+
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	if result.Vstring != "foo" {
+		t.Fatalf("bad: %#v", result.Vstring)
+	}
+}
+
+func TestDecode_NilInterfaceHook(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"w": "",
+	}
+
+	decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) {
+		if t.String() == "io.Writer" {
+			return nil, nil
+		}
+
+		return v, nil
+	}
+
+	var result NilInterface
+	config := &DecoderConfig{
+		DecodeHook: decodeHook,
+		Result:     &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.W != nil {
+		t.Errorf("W should be nil: %#v", result.W)
+	}
+}
+
+func TestDecode_NilPointerHook(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"value": "",
+	}
+
+	decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) {
+		if typed, ok := v.(string); ok {
+			if typed == "" {
+				return nil, nil
+			}
+		}
+		return v, nil
+	}
+
+	var result NilPointer
+	config := &DecoderConfig{
+		DecodeHook: decodeHook,
+		Result:     &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.Value != nil {
+		t.Errorf("W should be nil: %#v", result.Value)
+	}
+}
+
+func TestDecode_FuncHook(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"foo": "baz",
+	}
+
+	decodeHook := func(f, t reflect.Type, v interface{}) (interface{}, error) {
+		if t.Kind() != reflect.Func {
+			return v, nil
+		}
+		val := v.(string)
+		return func() string { return val }, nil
+	}
+
+	var result Func
+	config := &DecoderConfig{
+		DecodeHook: decodeHook,
+		Result:     &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.Foo() != "baz" {
+		t.Errorf("Foo call result should be 'baz': %s", result.Foo())
+	}
+}
+
+func TestDecode_NonStruct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"foo": "bar",
+		"bar": "baz",
+	}
+
+	var result map[string]string
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	if result["foo"] != "bar" {
+		t.Fatal("foo is not bar")
+	}
+}
+
+func TestDecode_StructMatch(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vbar": Basic{
+			Vstring: "foo",
+		},
+	}
+
+	var result Nested
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vbar.Vstring != "foo" {
+		t.Errorf("bad: %#v", result)
+	}
+}
+
+func TestDecode_TypeConversion(t *testing.T) {
+	input := map[string]interface{}{
+		"IntToFloat":         42,
+		"IntToUint":          42,
+		"IntToBool":          1,
+		"IntToString":        42,
+		"UintToInt":          42,
+		"UintToFloat":        42,
+		"UintToBool":         42,
+		"UintToString":       42,
+		"BoolToInt":          true,
+		"BoolToUint":         true,
+		"BoolToFloat":        true,
+		"BoolToString":       true,
+		"FloatToInt":         42.42,
+		"FloatToUint":        42.42,
+		"FloatToBool":        42.42,
+		"FloatToString":      42.42,
+		"SliceUint8ToString": []uint8("foo"),
+		"StringToSliceUint8": "foo",
+		"ArrayUint8ToString": [3]uint8{'f', 'o', 'o'},
+		"StringToInt":        "42",
+		"StringToUint":       "42",
+		"StringToBool":       "1",
+		"StringToFloat":      "42.42",
+		"StringToStrSlice":   "A",
+		"StringToIntSlice":   "42",
+		"StringToStrArray":   "A",
+		"StringToIntArray":   "42",
+		"SliceToMap":         []interface{}{},
+		"MapToSlice":         map[string]interface{}{},
+		"ArrayToMap":         []interface{}{},
+		"MapToArray":         map[string]interface{}{},
+	}
+
+	expectedResultStrict := TypeConversionResult{
+		IntToFloat:  42.0,
+		IntToUint:   42,
+		UintToInt:   42,
+		UintToFloat: 42,
+		BoolToInt:   0,
+		BoolToUint:  0,
+		BoolToFloat: 0,
+		FloatToInt:  42,
+		FloatToUint: 42,
+	}
+
+	expectedResultWeak := TypeConversionResult{
+		IntToFloat:         42.0,
+		IntToUint:          42,
+		IntToBool:          true,
+		IntToString:        "42",
+		UintToInt:          42,
+		UintToFloat:        42,
+		UintToBool:         true,
+		UintToString:       "42",
+		BoolToInt:          1,
+		BoolToUint:         1,
+		BoolToFloat:        1,
+		BoolToString:       "1",
+		FloatToInt:         42,
+		FloatToUint:        42,
+		FloatToBool:        true,
+		FloatToString:      "42.42",
+		SliceUint8ToString: "foo",
+		StringToSliceUint8: []byte("foo"),
+		ArrayUint8ToString: "foo",
+		StringToInt:        42,
+		StringToUint:       42,
+		StringToBool:       true,
+		StringToFloat:      42.42,
+		StringToStrSlice:   []string{"A"},
+		StringToIntSlice:   []int{42},
+		StringToStrArray:   [1]string{"A"},
+		StringToIntArray:   [1]int{42},
+		SliceToMap:         map[string]interface{}{},
+		MapToSlice:         []interface{}{},
+		ArrayToMap:         map[string]interface{}{},
+		MapToArray:         [1]interface{}{},
+	}
+
+	// Test strict type conversion
+	var resultStrict TypeConversionResult
+	err := Decode(input, &resultStrict)
+	if err == nil {
+		t.Errorf("should return an error")
+	}
+	if !reflect.DeepEqual(resultStrict, expectedResultStrict) {
+		t.Errorf("expected %v, got: %v", expectedResultStrict, resultStrict)
+	}
+
+	// Test weak type conversion
+	var decoder *Decoder
+	var resultWeak TypeConversionResult
+
+	config := &DecoderConfig{
+		WeaklyTypedInput: true,
+		Result:           &resultWeak,
+	}
+
+	decoder, err = NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if !reflect.DeepEqual(resultWeak, expectedResultWeak) {
+		t.Errorf("expected \n%#v, got: \n%#v", expectedResultWeak, resultWeak)
+	}
+}
+
+func TestDecoder_ErrorUnused(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "hello",
+		"foo":     "bar",
+	}
+
+	var result Basic
+	config := &DecoderConfig{
+		ErrorUnused: true,
+		Result:      &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err == nil {
+		t.Fatal("expected error")
+	}
+}
+
+func TestDecoder_ErrorUnused_NotSetable(t *testing.T) {
+	t.Parallel()
+
+	// lowercase vsilent is unexported and cannot be set
+	input := map[string]interface{}{
+		"vsilent": "false",
+	}
+
+	var result Basic
+	config := &DecoderConfig{
+		ErrorUnused: true,
+		Result:      &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err == nil {
+		t.Fatal("expected error")
+	}
+}
+func TestDecoder_ErrorUnset(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "hello",
+		"foo":     "bar",
+	}
+
+	var result Basic
+	config := &DecoderConfig{
+		ErrorUnset: true,
+		Result:     &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err == nil {
+		t.Fatal("expected error")
+	}
+}
+
+func TestMap(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vother": map[interface{}]interface{}{
+			"foo": "foo",
+			"bar": "bar",
+		},
+	}
+
+	var result Map
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an error: %s", err)
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vother == nil {
+		t.Fatal("vother should not be nil")
+	}
+
+	if len(result.Vother) != 2 {
+		t.Error("vother should have two items")
+	}
+
+	if result.Vother["foo"] != "foo" {
+		t.Errorf("'foo' key should be foo, got: %#v", result.Vother["foo"])
+	}
+
+	if result.Vother["bar"] != "bar" {
+		t.Errorf("'bar' key should be bar, got: %#v", result.Vother["bar"])
+	}
+}
+
+func TestMapMerge(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vother": map[interface{}]interface{}{
+			"foo": "foo",
+			"bar": "bar",
+		},
+	}
+
+	var result Map
+	result.Vother = map[string]string{"hello": "world"}
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an error: %s", err)
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	expected := map[string]string{
+		"foo":   "foo",
+		"bar":   "bar",
+		"hello": "world",
+	}
+	if !reflect.DeepEqual(result.Vother, expected) {
+		t.Errorf("bad: %#v", result.Vother)
+	}
+}
+
+func TestMapOfStruct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"value": map[string]interface{}{
+			"foo": map[string]string{"vstring": "one"},
+			"bar": map[string]string{"vstring": "two"},
+		},
+	}
+
+	var result MapOfStruct
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err)
+	}
+
+	if result.Value == nil {
+		t.Fatal("value should not be nil")
+	}
+
+	if len(result.Value) != 2 {
+		t.Error("value should have two items")
+	}
+
+	if result.Value["foo"].Vstring != "one" {
+		t.Errorf("foo value should be 'one', got: %s", result.Value["foo"].Vstring)
+	}
+
+	if result.Value["bar"].Vstring != "two" {
+		t.Errorf("bar value should be 'two', got: %s", result.Value["bar"].Vstring)
+	}
+}
+
+func TestNestedType(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": map[string]interface{}{
+			"vstring": "foo",
+			"vint":    42,
+			"vbool":   true,
+		},
+	}
+
+	var result Nested
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vbar.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+	}
+
+	if result.Vbar.Vint != 42 {
+		t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+	}
+
+	if result.Vbar.Vbool != true {
+		t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+	}
+
+	if result.Vbar.Vextra != "" {
+		t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+	}
+}
+
+func TestNestedTypePointer(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": &map[string]interface{}{
+			"vstring": "foo",
+			"vint":    42,
+			"vbool":   true,
+		},
+	}
+
+	var result NestedPointer
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vbar.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+	}
+
+	if result.Vbar.Vint != 42 {
+		t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+	}
+
+	if result.Vbar.Vbool != true {
+		t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+	}
+
+	if result.Vbar.Vextra != "" {
+		t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+	}
+}
+
+// Test for issue #46.
+func TestNestedTypeInterface(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": &map[string]interface{}{
+			"vstring": "foo",
+			"vint":    42,
+			"vbool":   true,
+
+			"vdata": map[string]interface{}{
+				"vstring": "bar",
+			},
+		},
+	}
+
+	var result NestedPointer
+	result.Vbar = new(Basic)
+	result.Vbar.Vdata = new(Basic)
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got an err: %s", err.Error())
+	}
+
+	if result.Vfoo != "foo" {
+		t.Errorf("vfoo value should be 'foo': %#v", result.Vfoo)
+	}
+
+	if result.Vbar.Vstring != "foo" {
+		t.Errorf("vstring value should be 'foo': %#v", result.Vbar.Vstring)
+	}
+
+	if result.Vbar.Vint != 42 {
+		t.Errorf("vint value should be 42: %#v", result.Vbar.Vint)
+	}
+
+	if result.Vbar.Vbool != true {
+		t.Errorf("vbool value should be true: %#v", result.Vbar.Vbool)
+	}
+
+	if result.Vbar.Vextra != "" {
+		t.Errorf("vextra value should be empty: %#v", result.Vbar.Vextra)
+	}
+
+	if result.Vbar.Vdata.(*Basic).Vstring != "bar" {
+		t.Errorf("vstring value should be 'bar': %#v", result.Vbar.Vdata.(*Basic).Vstring)
+	}
+}
+
+func TestSlice(t *testing.T) {
+	t.Parallel()
+
+	inputStringSlice := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": []string{"foo", "bar", "baz"},
+	}
+
+	inputStringSlicePointer := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": &[]string{"foo", "bar", "baz"},
+	}
+
+	outputStringSlice := &Slice{
+		"foo",
+		[]string{"foo", "bar", "baz"},
+	}
+
+	testSliceInput(t, inputStringSlice, outputStringSlice)
+	testSliceInput(t, inputStringSlicePointer, outputStringSlice)
+}
+
+func TestInvalidSlice(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": 42,
+	}
+
+	result := Slice{}
+	err := Decode(input, &result)
+	if err == nil {
+		t.Errorf("expected failure")
+	}
+}
+
+func TestSliceOfStruct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"value": []map[string]interface{}{
+			{"vstring": "one"},
+			{"vstring": "two"},
+		},
+	}
+
+	var result SliceOfStruct
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got unexpected error: %s", err)
+	}
+
+	if len(result.Value) != 2 {
+		t.Fatalf("expected two values, got %d", len(result.Value))
+	}
+
+	if result.Value[0].Vstring != "one" {
+		t.Errorf("first value should be 'one', got: %s", result.Value[0].Vstring)
+	}
+
+	if result.Value[1].Vstring != "two" {
+		t.Errorf("second value should be 'two', got: %s", result.Value[1].Vstring)
+	}
+}
+
+func TestSliceCornerCases(t *testing.T) {
+	t.Parallel()
+
+	// Input with a map with zero values
+	input := map[string]interface{}{}
+	var resultWeak []Basic
+
+	err := WeakDecode(input, &resultWeak)
+	if err != nil {
+		t.Fatalf("got unexpected error: %s", err)
+	}
+
+	if len(resultWeak) != 0 {
+		t.Errorf("length should be 0")
+	}
+	// Input with more values
+	input = map[string]interface{}{
+		"Vstring": "foo",
+	}
+
+	resultWeak = nil
+	err = WeakDecode(input, &resultWeak)
+	if err != nil {
+		t.Fatalf("got unexpected error: %s", err)
+	}
+
+	if resultWeak[0].Vstring != "foo" {
+		t.Errorf("value does not match")
+	}
+}
+
+func TestSliceToMap(t *testing.T) {
+	t.Parallel()
+
+	input := []map[string]interface{}{
+		{
+			"foo": "bar",
+		},
+		{
+			"bar": "baz",
+		},
+	}
+
+	var result map[string]interface{}
+	err := WeakDecode(input, &result)
+	if err != nil {
+		t.Fatalf("got an error: %s", err)
+	}
+
+	expected := map[string]interface{}{
+		"foo": "bar",
+		"bar": "baz",
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("bad: %#v", result)
+	}
+}
+
+func TestArray(t *testing.T) {
+	t.Parallel()
+
+	inputStringArray := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": [2]string{"foo", "bar"},
+	}
+
+	inputStringArrayPointer := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": &[2]string{"foo", "bar"},
+	}
+
+	outputStringArray := &Array{
+		"foo",
+		[2]string{"foo", "bar"},
+	}
+
+	testArrayInput(t, inputStringArray, outputStringArray)
+	testArrayInput(t, inputStringArrayPointer, outputStringArray)
+}
+
+func TestInvalidArray(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": 42,
+	}
+
+	result := Array{}
+	err := Decode(input, &result)
+	if err == nil {
+		t.Errorf("expected failure")
+	}
+}
+
+func TestArrayOfStruct(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"value": []map[string]interface{}{
+			{"vstring": "one"},
+			{"vstring": "two"},
+		},
+	}
+
+	var result ArrayOfStruct
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got unexpected error: %s", err)
+	}
+
+	if len(result.Value) != 2 {
+		t.Fatalf("expected two values, got %d", len(result.Value))
+	}
+
+	if result.Value[0].Vstring != "one" {
+		t.Errorf("first value should be 'one', got: %s", result.Value[0].Vstring)
+	}
+
+	if result.Value[1].Vstring != "two" {
+		t.Errorf("second value should be 'two', got: %s", result.Value[1].Vstring)
+	}
+}
+
+func TestArrayToMap(t *testing.T) {
+	t.Parallel()
+
+	input := []map[string]interface{}{
+		{
+			"foo": "bar",
+		},
+		{
+			"bar": "baz",
+		},
+	}
+
+	var result map[string]interface{}
+	err := WeakDecode(input, &result)
+	if err != nil {
+		t.Fatalf("got an error: %s", err)
+	}
+
+	expected := map[string]interface{}{
+		"foo": "bar",
+		"bar": "baz",
+	}
+	if !reflect.DeepEqual(result, expected) {
+		t.Errorf("bad: %#v", result)
+	}
+}
+
+func TestDecodeTable(t *testing.T) {
+	t.Parallel()
+
+	// We need to make new types so that we don't get the short-circuit
+	// copy functionality. We want to test the deep copying functionality.
+	type BasicCopy Basic
+	type NestedPointerCopy NestedPointer
+	type MapCopy Map
+
+	tests := []struct {
+		name    string
+		in      interface{}
+		target  interface{}
+		out     interface{}
+		wantErr bool
+	}{
+		{
+			"basic struct input",
+			&Basic{
+				Vstring: "vstring",
+				Vint:    2,
+				Vint8:   2,
+				Vint16:  2,
+				Vint32:  2,
+				Vint64:  2,
+				Vuint:   3,
+				Vbool:   true,
+				Vfloat:  4.56,
+				Vextra:  "vextra",
+				vsilent: true,
+				Vdata:   []byte("data"),
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{
+				"Vstring":     "vstring",
+				"Vint":        2,
+				"Vint8":       int8(2),
+				"Vint16":      int16(2),
+				"Vint32":      int32(2),
+				"Vint64":      int64(2),
+				"Vuint":       uint(3),
+				"Vbool":       true,
+				"Vfloat":      4.56,
+				"Vextra":      "vextra",
+				"Vdata":       []byte("data"),
+				"VjsonInt":    0,
+				"VjsonUint":   uint(0),
+				"VjsonUint64": uint64(0),
+				"VjsonFloat":  0.0,
+				"VjsonNumber": json.Number(""),
+			},
+			false,
+		},
+		{
+			"embedded struct input",
+			&Embedded{
+				Vunique: "vunique",
+				Basic: Basic{
+					Vstring: "vstring",
+					Vint:    2,
+					Vint8:   2,
+					Vint16:  2,
+					Vint32:  2,
+					Vint64:  2,
+					Vuint:   3,
+					Vbool:   true,
+					Vfloat:  4.56,
+					Vextra:  "vextra",
+					vsilent: true,
+					Vdata:   []byte("data"),
+				},
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{
+				"Vunique": "vunique",
+				"Basic": map[string]interface{}{
+					"Vstring":     "vstring",
+					"Vint":        2,
+					"Vint8":       int8(2),
+					"Vint16":      int16(2),
+					"Vint32":      int32(2),
+					"Vint64":      int64(2),
+					"Vuint":       uint(3),
+					"Vbool":       true,
+					"Vfloat":      4.56,
+					"Vextra":      "vextra",
+					"Vdata":       []byte("data"),
+					"VjsonInt":    0,
+					"VjsonUint":   uint(0),
+					"VjsonUint64": uint64(0),
+					"VjsonFloat":  0.0,
+					"VjsonNumber": json.Number(""),
+				},
+			},
+			false,
+		},
+		{
+			"struct => struct",
+			&Basic{
+				Vstring: "vstring",
+				Vint:    2,
+				Vuint:   3,
+				Vbool:   true,
+				Vfloat:  4.56,
+				Vextra:  "vextra",
+				Vdata:   []byte("data"),
+				vsilent: true,
+			},
+			&BasicCopy{},
+			&BasicCopy{
+				Vstring: "vstring",
+				Vint:    2,
+				Vuint:   3,
+				Vbool:   true,
+				Vfloat:  4.56,
+				Vextra:  "vextra",
+				Vdata:   []byte("data"),
+			},
+			false,
+		},
+		{
+			"struct => struct with pointers",
+			&NestedPointer{
+				Vfoo: "hello",
+				Vbar: nil,
+			},
+			&NestedPointerCopy{},
+			&NestedPointerCopy{
+				Vfoo: "hello",
+			},
+			false,
+		},
+		{
+			"basic pointer to non-pointer",
+			&BasicPointer{
+				Vstring: stringPtr("vstring"),
+				Vint:    intPtr(2),
+				Vuint:   uintPtr(3),
+				Vbool:   boolPtr(true),
+				Vfloat:  floatPtr(4.56),
+				Vdata:   interfacePtr([]byte("data")),
+			},
+			&Basic{},
+			&Basic{
+				Vstring: "vstring",
+				Vint:    2,
+				Vuint:   3,
+				Vbool:   true,
+				Vfloat:  4.56,
+				Vdata:   []byte("data"),
+			},
+			false,
+		},
+		{
+			"slice non-pointer to pointer",
+			&Slice{},
+			&SlicePointer{},
+			&SlicePointer{},
+			false,
+		},
+		{
+			"slice non-pointer to pointer, zero field",
+			&Slice{},
+			&SlicePointer{
+				Vbar: &[]string{"yo"},
+			},
+			&SlicePointer{},
+			false,
+		},
+		{
+			"slice to slice alias",
+			&Slice{},
+			&SliceOfAlias{},
+			&SliceOfAlias{},
+			false,
+		},
+		{
+			"nil map to map",
+			&Map{},
+			&MapCopy{},
+			&MapCopy{},
+			false,
+		},
+		{
+			"nil map to non-empty map",
+			&Map{},
+			&MapCopy{Vother: map[string]string{"foo": "bar"}},
+			&MapCopy{},
+			false,
+		},
+
+		{
+			"slice input - should error",
+			[]string{"foo", "bar"},
+			&map[string]interface{}{},
+			&map[string]interface{}{},
+			true,
+		},
+		{
+			"struct with slice property",
+			&Slice{
+				Vfoo: "vfoo",
+				Vbar: []string{"foo", "bar"},
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{
+				"Vfoo": "vfoo",
+				"Vbar": []string{"foo", "bar"},
+			},
+			false,
+		},
+		{
+			"struct with empty slice",
+			&map[string]interface{}{
+				"Vbar": []string{},
+			},
+			&Slice{},
+			&Slice{
+				Vbar: []string{},
+			},
+			false,
+		},
+		{
+			"struct with slice of struct property",
+			&SliceOfStruct{
+				Value: []Basic{
+					Basic{
+						Vstring: "vstring",
+						Vint:    2,
+						Vuint:   3,
+						Vbool:   true,
+						Vfloat:  4.56,
+						Vextra:  "vextra",
+						vsilent: true,
+						Vdata:   []byte("data"),
+					},
+				},
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{
+				"Value": []Basic{
+					Basic{
+						Vstring: "vstring",
+						Vint:    2,
+						Vuint:   3,
+						Vbool:   true,
+						Vfloat:  4.56,
+						Vextra:  "vextra",
+						vsilent: true,
+						Vdata:   []byte("data"),
+					},
+				},
+			},
+			false,
+		},
+		{
+			"struct with map property",
+			&Map{
+				Vfoo:   "vfoo",
+				Vother: map[string]string{"vother": "vother"},
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{
+				"Vfoo": "vfoo",
+				"Vother": map[string]string{
+					"vother": "vother",
+				}},
+			false,
+		},
+		{
+			"tagged struct",
+			&Tagged{
+				Extra: "extra",
+				Value: "value",
+			},
+			&map[string]string{},
+			&map[string]string{
+				"bar": "extra",
+				"foo": "value",
+			},
+			false,
+		},
+		{
+			"omit tag struct",
+			&struct {
+				Value string `mapstructure:"value"`
+				Omit  string `mapstructure:"-"`
+			}{
+				Value: "value",
+				Omit:  "omit",
+			},
+			&map[string]string{},
+			&map[string]string{
+				"value": "value",
+			},
+			false,
+		},
+		{
+			"decode to wrong map type",
+			&struct {
+				Value string
+			}{
+				Value: "string",
+			},
+			&map[string]int{},
+			&map[string]int{},
+			true,
+		},
+		{
+			"remainder",
+			map[string]interface{}{
+				"A": "hello",
+				"B": "goodbye",
+				"C": "yo",
+			},
+			&Remainder{},
+			&Remainder{
+				A: "hello",
+				Extra: map[string]interface{}{
+					"B": "goodbye",
+					"C": "yo",
+				},
+			},
+			false,
+		},
+		{
+			"remainder with no extra",
+			map[string]interface{}{
+				"A": "hello",
+			},
+			&Remainder{},
+			&Remainder{
+				A:     "hello",
+				Extra: nil,
+			},
+			false,
+		},
+		{
+			"struct with omitempty tag return non-empty values",
+			&struct {
+				VisibleField interface{} `mapstructure:"visible"`
+				OmitField    interface{} `mapstructure:"omittable,omitempty"`
+			}{
+				VisibleField: nil,
+				OmitField:    "string",
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{"visible": nil, "omittable": "string"},
+			false,
+		},
+		{
+			"struct with omitempty tag ignore empty values",
+			&struct {
+				VisibleField interface{} `mapstructure:"visible"`
+				OmitField    interface{} `mapstructure:"omittable,omitempty"`
+			}{
+				VisibleField: nil,
+				OmitField:    nil,
+			},
+			&map[string]interface{}{},
+			&map[string]interface{}{"visible": nil},
+			false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := Decode(tt.in, tt.target); (err != nil) != tt.wantErr {
+				t.Fatalf("%q: TestMapOutputForStructuredInputs() unexpected error: %s", tt.name, err)
+			}
+
+			if !reflect.DeepEqual(tt.out, tt.target) {
+				t.Fatalf("%q: TestMapOutputForStructuredInputs() expected: %#v, got: %#v", tt.name, tt.out, tt.target)
+			}
+		})
+	}
+}
+
+func TestInvalidType(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": 42,
+	}
+
+	var result Basic
+	err := Decode(input, &result)
+	if err == nil {
+		t.Fatal("error should exist")
+	}
+
+	derr, ok := err.(*Error)
+	if !ok {
+		t.Fatalf("error should be kind of Error, instead: %#v", err)
+	}
+
+	if derr.Errors[0] !=
+		"'Vstring' expected type 'string', got unconvertible type 'int', value: '42'" {
+		t.Errorf("got unexpected error: %s", err)
+	}
+
+	inputNegIntUint := map[string]interface{}{
+		"vuint": -42,
+	}
+
+	err = Decode(inputNegIntUint, &result)
+	if err == nil {
+		t.Fatal("error should exist")
+	}
+
+	derr, ok = err.(*Error)
+	if !ok {
+		t.Fatalf("error should be kind of Error, instead: %#v", err)
+	}
+
+	if derr.Errors[0] != "cannot parse 'Vuint', -42 overflows uint" {
+		t.Errorf("got unexpected error: %s", err)
+	}
+
+	inputNegFloatUint := map[string]interface{}{
+		"vuint": -42.0,
+	}
+
+	err = Decode(inputNegFloatUint, &result)
+	if err == nil {
+		t.Fatal("error should exist")
+	}
+
+	derr, ok = err.(*Error)
+	if !ok {
+		t.Fatalf("error should be kind of Error, instead: %#v", err)
+	}
+
+	if derr.Errors[0] != "cannot parse 'Vuint', -42.000000 overflows uint" {
+		t.Errorf("got unexpected error: %s", err)
+	}
+}
+
+func TestDecodeMetadata(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": map[string]interface{}{
+			"vstring": "foo",
+			"Vuint":   42,
+			"vsilent": "false",
+			"foo":     "bar",
+		},
+		"bar": "nil",
+	}
+
+	var md Metadata
+	var result Nested
+
+	err := DecodeMetadata(input, &result, &md)
+	if err != nil {
+		t.Fatalf("err: %s", err.Error())
+	}
+
+	expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"}
+	sort.Strings(md.Keys)
+	if !reflect.DeepEqual(md.Keys, expectedKeys) {
+		t.Fatalf("bad keys: %#v", md.Keys)
+	}
+
+	expectedUnused := []string{"Vbar.foo", "Vbar.vsilent", "bar"}
+	sort.Strings(md.Unused)
+	if !reflect.DeepEqual(md.Unused, expectedUnused) {
+		t.Fatalf("bad unused: %#v", md.Unused)
+	}
+}
+
+func TestMetadata(t *testing.T) {
+	t.Parallel()
+
+	type testResult struct {
+		Vfoo string
+		Vbar BasicPointer
+	}
+
+	input := map[string]interface{}{
+		"vfoo": "foo",
+		"vbar": map[string]interface{}{
+			"vstring": "foo",
+			"Vuint":   42,
+			"vsilent": "false",
+			"foo":     "bar",
+		},
+		"bar": "nil",
+	}
+
+	var md Metadata
+	var result testResult
+	config := &DecoderConfig{
+		Metadata: &md,
+		Result:   &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("err: %s", err.Error())
+	}
+
+	expectedKeys := []string{"Vbar", "Vbar.Vstring", "Vbar.Vuint", "Vfoo"}
+	sort.Strings(md.Keys)
+	if !reflect.DeepEqual(md.Keys, expectedKeys) {
+		t.Fatalf("bad keys: %#v", md.Keys)
+	}
+
+	expectedUnused := []string{"Vbar.foo", "Vbar.vsilent", "bar"}
+	sort.Strings(md.Unused)
+	if !reflect.DeepEqual(md.Unused, expectedUnused) {
+		t.Fatalf("bad unused: %#v", md.Unused)
+	}
+
+	expectedUnset := []string{
+		"Vbar.Vbool", "Vbar.Vdata", "Vbar.Vextra", "Vbar.Vfloat", "Vbar.Vint",
+		"Vbar.VjsonFloat", "Vbar.VjsonInt", "Vbar.VjsonNumber"}
+	sort.Strings(md.Unset)
+	if !reflect.DeepEqual(md.Unset, expectedUnset) {
+		t.Fatalf("bad unset: %#v", md.Unset)
+	}
+}
+
+func TestMetadata_Embedded(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"vstring": "foo",
+		"vunique": "bar",
+	}
+
+	var md Metadata
+	var result EmbeddedSquash
+	config := &DecoderConfig{
+		Metadata: &md,
+		Result:   &result,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("err: %s", err.Error())
+	}
+
+	expectedKeys := []string{"Vstring", "Vunique"}
+
+	sort.Strings(md.Keys)
+	if !reflect.DeepEqual(md.Keys, expectedKeys) {
+		t.Fatalf("bad keys: %#v", md.Keys)
+	}
+
+	expectedUnused := []string{}
+	if !reflect.DeepEqual(md.Unused, expectedUnused) {
+		t.Fatalf("bad unused: %#v", md.Unused)
+	}
+}
+
+func TestNonPtrValue(t *testing.T) {
+	t.Parallel()
+
+	err := Decode(map[string]interface{}{}, Basic{})
+	if err == nil {
+		t.Fatal("error should exist")
+	}
+
+	if err.Error() != "result must be a pointer" {
+		t.Errorf("got unexpected error: %s", err)
+	}
+}
+
+func TestTagged(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"foo": "bar",
+		"bar": "value",
+	}
+
+	var result Tagged
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+
+	if result.Value != "bar" {
+		t.Errorf("value should be 'bar', got: %#v", result.Value)
+	}
+
+	if result.Extra != "value" {
+		t.Errorf("extra should be 'value', got: %#v", result.Extra)
+	}
+}
+
+func TestWeakDecode(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"foo": "4",
+		"bar": "value",
+	}
+
+	var result struct {
+		Foo int
+		Bar string
+	}
+
+	if err := WeakDecode(input, &result); err != nil {
+		t.Fatalf("err: %s", err)
+	}
+	if result.Foo != 4 {
+		t.Fatalf("bad: %#v", result)
+	}
+	if result.Bar != "value" {
+		t.Fatalf("bad: %#v", result)
+	}
+}
+
+func TestWeakDecodeMetadata(t *testing.T) {
+	t.Parallel()
+
+	input := map[string]interface{}{
+		"foo":        "4",
+		"bar":        "value",
+		"unused":     "value",
+		"unexported": "value",
+	}
+
+	var md Metadata
+	var result struct {
+		Foo        int
+		Bar        string
+		unexported string
+	}
+
+	if err := WeakDecodeMetadata(input, &result, &md); err != nil {
+		t.Fatalf("err: %s", err)
+	}
+	if result.Foo != 4 {
+		t.Fatalf("bad: %#v", result)
+	}
+	if result.Bar != "value" {
+		t.Fatalf("bad: %#v", result)
+	}
+
+	expectedKeys := []string{"Bar", "Foo"}
+	sort.Strings(md.Keys)
+	if !reflect.DeepEqual(md.Keys, expectedKeys) {
+		t.Fatalf("bad keys: %#v", md.Keys)
+	}
+
+	expectedUnused := []string{"unexported", "unused"}
+	sort.Strings(md.Unused)
+	if !reflect.DeepEqual(md.Unused, expectedUnused) {
+		t.Fatalf("bad unused: %#v", md.Unused)
+	}
+}
+
+func TestDecode_StructTaggedWithOmitempty_OmitEmptyValues(t *testing.T) {
+	t.Parallel()
+
+	input := &StructWithOmitEmpty{}
+
+	var emptySlice []interface{}
+	var emptyMap map[string]interface{}
+	var emptyNested *Nested
+	expected := &map[string]interface{}{
+		"visible-string": "",
+		"visible-int":    0,
+		"visible-float":  0.0,
+		"visible-slice":  emptySlice,
+		"visible-map":    emptyMap,
+		"visible-nested": emptyNested,
+	}
+
+	actual := &map[string]interface{}{}
+	Decode(input, actual)
+
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual)
+	}
+}
+
+func TestDecode_StructTaggedWithOmitempty_KeepNonEmptyValues(t *testing.T) {
+	t.Parallel()
+
+	input := &StructWithOmitEmpty{
+		VisibleStringField: "",
+		OmitStringField:    "string",
+		VisibleIntField:    0,
+		OmitIntField:       1,
+		VisibleFloatField:  0.0,
+		OmitFloatField:     1.0,
+		VisibleSliceField:  nil,
+		OmitSliceField:     []interface{}{1},
+		VisibleMapField:    nil,
+		OmitMapField:       map[string]interface{}{"k": "v"},
+		NestedField:        nil,
+		OmitNestedField:    &Nested{},
+	}
+
+	var emptySlice []interface{}
+	var emptyMap map[string]interface{}
+	var emptyNested *Nested
+	expected := &map[string]interface{}{
+		"visible-string":   "",
+		"omittable-string": "string",
+		"visible-int":      0,
+		"omittable-int":    1,
+		"visible-float":    0.0,
+		"omittable-float":  1.0,
+		"visible-slice":    emptySlice,
+		"omittable-slice":  []interface{}{1},
+		"visible-map":      emptyMap,
+		"omittable-map":    map[string]interface{}{"k": "v"},
+		"visible-nested":   emptyNested,
+		"omittable-nested": &Nested{},
+	}
+
+	actual := &map[string]interface{}{}
+	Decode(input, actual)
+
+	if !reflect.DeepEqual(actual, expected) {
+		t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual)
+	}
+}
+
+func TestDecode_mapToStruct(t *testing.T) {
+	type Target struct {
+		String    string
+		StringPtr *string
+	}
+
+	expected := Target{
+		String: "hello",
+	}
+
+	var target Target
+	err := Decode(map[string]interface{}{
+		"string":    "hello",
+		"StringPtr": "goodbye",
+	}, &target)
+	if err != nil {
+		t.Fatalf("got error: %s", err)
+	}
+
+	// Pointers fail reflect test so do those manually
+	if target.StringPtr == nil || *target.StringPtr != "goodbye" {
+		t.Fatalf("bad: %#v", target)
+	}
+	target.StringPtr = nil
+
+	if !reflect.DeepEqual(target, expected) {
+		t.Fatalf("bad: %#v", target)
+	}
+}
+
+func TestDecoder_MatchName(t *testing.T) {
+	t.Parallel()
+
+	type Target struct {
+		FirstMatch  string `mapstructure:"first_match"`
+		SecondMatch string
+		NoMatch     string `mapstructure:"no_match"`
+	}
+
+	input := map[string]interface{}{
+		"first_match": "foo",
+		"SecondMatch": "bar",
+		"NO_MATCH":    "baz",
+	}
+
+	expected := Target{
+		FirstMatch:  "foo",
+		SecondMatch: "bar",
+	}
+
+	var actual Target
+	config := &DecoderConfig{
+		Result: &actual,
+		MatchName: func(mapKey, fieldName string) bool {
+			return mapKey == fieldName
+		},
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Fatalf("Decode() expected: %#v, got: %#v", expected, actual)
+	}
+}
+
+func TestDecoder_IgnoreUntaggedFields(t *testing.T) {
+	type Input struct {
+		UntaggedNumber int
+		TaggedNumber   int `mapstructure:"tagged_number"`
+		UntaggedString string
+		TaggedString   string `mapstructure:"tagged_string"`
+	}
+	input := &Input{
+		UntaggedNumber: 31,
+		TaggedNumber:   42,
+		UntaggedString: "hidden",
+		TaggedString:   "visible",
+	}
+
+	actual := make(map[string]interface{})
+	config := &DecoderConfig{
+		Result:               &actual,
+		IgnoreUntaggedFields: true,
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	err = decoder.Decode(input)
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	expected := map[string]interface{}{
+		"tagged_number": 42,
+		"tagged_string": "visible",
+	}
+
+	if !reflect.DeepEqual(expected, actual) {
+		t.Fatalf("Decode() expected: %#v\ngot: %#v", expected, actual)
+	}
+}
+
+func testSliceInput(t *testing.T, input map[string]interface{}, expected *Slice) {
+	var result Slice
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got error: %s", err)
+	}
+
+	if result.Vfoo != expected.Vfoo {
+		t.Errorf("Vfoo expected '%s', got '%s'", expected.Vfoo, result.Vfoo)
+	}
+
+	if result.Vbar == nil {
+		t.Fatalf("Vbar a slice, got '%#v'", result.Vbar)
+	}
+
+	if len(result.Vbar) != len(expected.Vbar) {
+		t.Errorf("Vbar length should be %d, got %d", len(expected.Vbar), len(result.Vbar))
+	}
+
+	for i, v := range result.Vbar {
+		if v != expected.Vbar[i] {
+			t.Errorf(
+				"Vbar[%d] should be '%#v', got '%#v'",
+				i, expected.Vbar[i], v)
+		}
+	}
+}
+
+func testArrayInput(t *testing.T, input map[string]interface{}, expected *Array) {
+	var result Array
+	err := Decode(input, &result)
+	if err != nil {
+		t.Fatalf("got error: %s", err)
+	}
+
+	if result.Vfoo != expected.Vfoo {
+		t.Errorf("Vfoo expected '%s', got '%s'", expected.Vfoo, result.Vfoo)
+	}
+
+	if result.Vbar == [2]string{} {
+		t.Fatalf("Vbar a slice, got '%#v'", result.Vbar)
+	}
+
+	if len(result.Vbar) != len(expected.Vbar) {
+		t.Errorf("Vbar length should be %d, got %d", len(expected.Vbar), len(result.Vbar))
+	}
+
+	for i, v := range result.Vbar {
+		if v != expected.Vbar[i] {
+			t.Errorf(
+				"Vbar[%d] should be '%#v', got '%#v'",
+				i, expected.Vbar[i], v)
+		}
+	}
+}
+
+func stringPtr(v string) *string              { return &v }
+func intPtr(v int) *int                       { return &v }
+func uintPtr(v uint) *uint                    { return &v }
+func boolPtr(v bool) *bool                    { return &v }
+func floatPtr(v float64) *float64             { return &v }
+func interfacePtr(v interface{}) *interface{} { return &v }
diff --git a/app/utils/mapstructure/my_decode.go b/app/utils/mapstructure/my_decode.go
new file mode 100644
index 0000000..6cafe11
--- /dev/null
+++ b/app/utils/mapstructure/my_decode.go
@@ -0,0 +1,26 @@
+package mapstructure
+
+import "time"
+
+// DecodeWithTime 支持时间转字符串
+// 支持
+// 1. *Time.time 转 string/*string
+// 2. *Time.time 转 uint/uint32/uint64/int/int32/int64,支持带指针
+// 不能用 Time.time 转,它会在上层认为是一个结构体数据而直接转成map,再到hook方法
+func DecodeWithTime(input, output interface{}, layout string) error {
+	if layout == "" {
+		layout = time.DateTime
+	}
+	config := &DecoderConfig{
+		Metadata:   nil,
+		Result:     output,
+		DecodeHook: ComposeDecodeHookFunc(TimeToStringHook(layout), TimeToUnixIntHook()),
+	}
+
+	decoder, err := NewDecoder(config)
+	if err != nil {
+		return err
+	}
+
+	return decoder.Decode(input)
+}
diff --git a/app/utils/mapstructure/my_decode_hook.go b/app/utils/mapstructure/my_decode_hook.go
new file mode 100644
index 0000000..4873731
--- /dev/null
+++ b/app/utils/mapstructure/my_decode_hook.go
@@ -0,0 +1,101 @@
+package mapstructure
+
+import (
+	"reflect"
+	"time"
+)
+
+// TimeToStringHook 时间转字符串
+// 支持 *Time.time 转 string/*string
+// 不能用 Time.time 转,它会在上层认为是一个结构体数据而直接转成map,再到hook方法
+func TimeToStringHook(layout string) DecodeHookFunc {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+		// 判断目标类型是否为字符串
+		var strType string
+		var isStrPointer *bool // 要转换的目标类型是否为指针字符串
+		if t == reflect.TypeOf(strType) {
+			isStrPointer = new(bool)
+		} else if t == reflect.TypeOf(&strType) {
+			isStrPointer = new(bool)
+			*isStrPointer = true
+		}
+		if isStrPointer == nil {
+			return data, nil
+		}
+
+		// 判断类型是否为时间
+		timeType := time.Time{}
+		if f != reflect.TypeOf(timeType) && f != reflect.TypeOf(&timeType) {
+			return data, nil
+		}
+
+		// 将时间转换为字符串
+		var output string
+		switch v := data.(type) {
+		case *time.Time:
+			output = v.Format(layout)
+		case time.Time:
+			output = v.Format(layout)
+		default:
+			return data, nil
+		}
+
+		if *isStrPointer {
+			return &output, nil
+		}
+		return output, nil
+	}
+}
+
+// TimeToUnixIntHook 时间转时间戳
+// 支持 *Time.time 转 uint/uint32/uint64/int/int32/int64,支持带指针
+// 不能用 Time.time 转,它会在上层认为是一个结构体数据而直接转成map,再到hook方法
+func TimeToUnixIntHook() DecodeHookFunc {
+	return func(
+		f reflect.Type,
+		t reflect.Type,
+		data interface{}) (interface{}, error) {
+
+		tkd := t.Kind()
+		if tkd != reflect.Int && tkd != reflect.Int32 && tkd != reflect.Int64 &&
+			tkd != reflect.Uint && tkd != reflect.Uint32 && tkd != reflect.Uint64 {
+			return data, nil
+		}
+
+		// 判断类型是否为时间
+		timeType := time.Time{}
+		if f != reflect.TypeOf(timeType) && f != reflect.TypeOf(&timeType) {
+			return data, nil
+		}
+
+		// 将时间转换为字符串
+		var output int64
+		switch v := data.(type) {
+		case *time.Time:
+			output = v.Unix()
+		case time.Time:
+			output = v.Unix()
+		default:
+			return data, nil
+		}
+		switch tkd {
+		case reflect.Int:
+			return int(output), nil
+		case reflect.Int32:
+			return int32(output), nil
+		case reflect.Int64:
+			return output, nil
+		case reflect.Uint:
+			return uint(output), nil
+		case reflect.Uint32:
+			return uint32(output), nil
+		case reflect.Uint64:
+			return uint64(output), nil
+		default:
+			return data, nil
+		}
+	}
+}
diff --git a/app/utils/mapstructure/my_decode_hook_test.go b/app/utils/mapstructure/my_decode_hook_test.go
new file mode 100644
index 0000000..fdf28f8
--- /dev/null
+++ b/app/utils/mapstructure/my_decode_hook_test.go
@@ -0,0 +1,274 @@
+package mapstructure
+
+import (
+	"testing"
+	"time"
+)
+
+func Test_TimeToStringHook(t *testing.T) {
+	type Input struct {
+		Time time.Time
+		Id   int
+	}
+
+	type InputTPointer struct {
+		Time *time.Time
+		Id   int
+	}
+
+	type Output struct {
+		Time string
+		Id   int
+	}
+
+	type OutputTPointer struct {
+		Time *string
+		Id   int
+	}
+	now := time.Now()
+	target := now.Format("2006-01-02 15:04:05")
+	idValue := 1
+	tests := []struct {
+		input  any
+		output any
+		name   string
+		layout string
+	}{
+		{
+			name:   "测试Time.time转string",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output{},
+		},
+		{
+			name:   "测试*Time.time转*string",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: OutputTPointer{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			decoder, err := NewDecoder(&DecoderConfig{
+				DecodeHook: TimeToStringHook(tt.layout),
+				Result:     &tt.output,
+			})
+			if err != nil {
+				t.Errorf("NewDecoder() err = %v,want nil", err)
+			}
+
+			if i, isOk := tt.input.(Input); isOk {
+				err = decoder.Decode(i)
+			}
+			if i, isOk := tt.input.(InputTPointer); isOk {
+				err = decoder.Decode(&i)
+			}
+			if err != nil {
+				t.Errorf("Decode err = %v,want nil", err)
+			}
+			//验证测试值
+			if output, isOk := tt.output.(OutputTPointer); isOk {
+				if *output.Time != target {
+					t.Errorf("Decode output time = %v,want %v", *output.Time, target)
+				}
+				if output.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", output.Id, idValue)
+				}
+			}
+			if output, isOk := tt.output.(Output); isOk {
+				if output.Time != target {
+					t.Errorf("Decode output time = %v,want %v", output.Time, target)
+				}
+				if output.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", output.Id, idValue)
+				}
+			}
+		})
+	}
+}
+
+func Test_TimeToUnixIntHook(t *testing.T) {
+	type InputTPointer struct {
+		Time *time.Time
+		Id   int
+	}
+
+	type Output[T int | *int | int32 | *int32 | int64 | *int64 | uint | *uint] struct {
+		Time T
+		Id   int
+	}
+
+	type test struct {
+		input  any
+		output any
+		name   string
+		layout string
+	}
+
+	now := time.Now()
+	target := now.Unix()
+	idValue := 1
+	tests := []test{
+		{
+			name:   "测试Time.time转int",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[int]{},
+		},
+		{
+			name:   "测试Time.time转*int",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[*int]{},
+		},
+		{
+			name:   "测试Time.time转int32",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[int32]{},
+		},
+		{
+			name:   "测试Time.time转*int32",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[*int32]{},
+		},
+		{
+			name:   "测试Time.time转int64",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[int64]{},
+		},
+		{
+			name:   "测试Time.time转*int64",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[*int64]{},
+		},
+		{
+			name:   "测试Time.time转uint",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[uint]{},
+		},
+		{
+			name:   "测试Time.time转*uint",
+			layout: "2006-01-02 15:04:05",
+			input: InputTPointer{
+				Time: &now,
+				Id:   idValue,
+			},
+			output: Output[*uint]{},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			decoder, err := NewDecoder(&DecoderConfig{
+				DecodeHook: TimeToUnixIntHook(),
+				Result:     &tt.output,
+			})
+			if err != nil {
+				t.Errorf("NewDecoder() err = %v,want nil", err)
+			}
+
+			if i, isOk := tt.input.(InputTPointer); isOk {
+				err = decoder.Decode(i)
+			}
+			if i, isOk := tt.input.(InputTPointer); isOk {
+				err = decoder.Decode(&i)
+			}
+			if err != nil {
+				t.Errorf("Decode err = %v,want nil", err)
+			}
+
+			//验证测试值
+			switch v := tt.output.(type) {
+			case Output[int]:
+				if int64(v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[*int]:
+				if int64(*v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[int32]:
+				if int64(v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[*int32]:
+				if int64(*v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[int64]:
+				if int64(v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[*int64]:
+				if int64(*v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[uint]:
+				if int64(v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			case Output[*uint]:
+				if int64(*v.Time) != target {
+					t.Errorf("Decode output time = %v,want %v", v.Time, target)
+				}
+				if v.Id != idValue {
+					t.Errorf("Decode output id = %v,want %v", v.Id, idValue)
+				}
+			}
+
+		})
+	}
+}
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index 25ca23d..250c746 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -30,7 +30,7 @@ func Bootstrap(conf *config.Config) (err error) {
 	if err != nil {
 		return
 	}
-
+	db.GetDb().ShowSQL(true)
 	//注册redis服务
 	err = redis.Pr.Register(redis.SingletonMain, conf.Redis)
 	if err != nil {
diff --git a/go.mod b/go.mod
index 6006b94..dae8173 100644
--- a/go.mod
+++ b/go.mod
@@ -8,12 +8,16 @@ require (
 	github.com/Shopify/sarama v1.19.0
 	github.com/ahmetb/go-linq/v3 v3.2.0
 	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
+	github.com/bwmarrin/snowflake v0.3.0
+	github.com/duke-git/lancet/v2 v2.3.3
 	github.com/forgoer/openssl v1.6.0
 	github.com/gin-gonic/gin v1.7.7
+	github.com/go-kratos/kratos/v2 v2.8.2
 	github.com/go-playground/locales v0.14.0
 	github.com/go-playground/universal-translator v0.18.0
 	github.com/go-sql-driver/mysql v1.6.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
+	github.com/golang/protobuf v1.5.4
 	github.com/nacos-group/nacos-sdk-go/v2 v2.2.5
 	github.com/nats-io/nats.go v1.9.1
 	github.com/openzipkin/zipkin-go v0.2.2
@@ -27,8 +31,9 @@ require (
 	github.com/swaggo/swag v1.7.9
 	github.com/tjfoc/gmsm v1.4.1
 	github.com/valyala/fasthttp v1.31.0
-	google.golang.org/grpc v1.56.3
-	google.golang.org/protobuf v1.30.0
+	github.com/xuri/excelize/v2 v2.9.0
+	google.golang.org/grpc v1.61.1
+	google.golang.org/protobuf v1.33.0
 	gopkg.in/go-playground/validator.v9 v9.31.0
 	xorm.io/builder v0.3.9
 	xorm.io/xorm v1.2.5
@@ -57,17 +62,19 @@ require (
 	github.com/emirpasic/gods v1.12.0 // indirect
 	github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/go-kratos/aegis v0.2.0 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/jsonreference v0.19.6 // indirect
 	github.com/go-openapi/spec v0.20.4 // indirect
 	github.com/go-openapi/swag v0.19.15 // indirect
+	github.com/go-playground/form/v4 v4.2.0 // indirect
 	github.com/go-playground/validator/v10 v10.9.0 // indirect
 	github.com/go-redis/redis/v8 v8.11.4 // indirect
 	github.com/goccy/go-json v0.8.1 // indirect
 	github.com/golang/mock v1.6.0 // indirect
-	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
-	github.com/google/uuid v1.3.0 // indirect
+	github.com/google/uuid v1.4.0 // indirect
+	github.com/gorilla/mux v1.8.1 // indirect
 	github.com/hetiansu5/accesslog v1.0.0 // indirect
 	github.com/hetiansu5/cores v1.0.0 // indirect
 	github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
@@ -82,6 +89,7 @@ require (
 	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
 	github.com/nats-io/jwt v0.3.2 // indirect
 	github.com/nats-io/nkeys v0.1.3 // indirect
 	github.com/nats-io/nuid v1.0.1 // indirect
@@ -90,6 +98,8 @@ require (
 	github.com/prometheus/common v0.32.1 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
+	github.com/richardlehane/mscfb v1.0.4 // indirect
+	github.com/richardlehane/msoleps v1.0.4 // indirect
 	github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/syndtr/goleveldb v1.0.0 // indirect
@@ -99,20 +109,24 @@ require (
 	github.com/ugorji/go/codec v1.2.6 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/valyala/fasttemplate v1.2.1 // indirect
+	github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
+	github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.21.0 // indirect
-	golang.org/x/crypto v0.17.0 // indirect
-	golang.org/x/net v0.17.0 // indirect
-	golang.org/x/sync v0.1.0 // indirect
-	golang.org/x/sys v0.15.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/crypto v0.28.0 // indirect
+	golang.org/x/exp v0.0.0-20221208152030-732eee02a75a // indirect
+	golang.org/x/net v0.30.0 // indirect
+	golang.org/x/sync v0.8.0 // indirect
+	golang.org/x/sys v0.26.0 // indirect
+	golang.org/x/text v0.19.0 // indirect
 	golang.org/x/time v0.1.0 // indirect
-	golang.org/x/tools v0.6.0 // indirect
-	google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
+	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
 	gopkg.in/ini.v1 v1.66.2 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 	stathat.com/c/consistent v1.0.0 // indirect
 	xorm.io/core v0.7.3 // indirect
 )
diff --git a/go.sum b/go.sum
index becba70..4d15c6a 100644
--- a/go.sum
+++ b/go.sum
@@ -110,6 +110,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
 github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
 github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
 github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
+github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
+github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -146,6 +148,8 @@ github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/duke-git/lancet/v2 v2.3.3 h1:OhqzNzkbJBS9ZlWLo/C7g+WSAOAAyNj7p9CAiEHurUc=
+github.com/duke-git/lancet/v2 v2.3.3/go.mod h1:zGa2R4xswg6EG9I6WnyubDbFO/+A/RROxIbXcwryTsc=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
@@ -173,8 +177,9 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
 github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
 github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -195,6 +200,10 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ=
+github.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI=
+github.com/go-kratos/kratos/v2 v2.8.2 h1:EsEA7AmPQ2YQQ0FZrDWO2HgBNqeWM8z/mWKzS5UkQaQ=
+github.com/go-kratos/kratos/v2 v2.8.2/go.mod h1:+Vfe3FzF0d+BfMdajA11jT0rAyJWublRE/seZQNZVxE=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -213,6 +222,8 @@ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyr
 github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
 github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=
+github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
 github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
@@ -280,8 +291,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
@@ -300,8 +311,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -321,8 +332,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
+github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -330,8 +342,9 @@ github.com/gopherjs/gopherjs v0.0.0-20211111143520-d0d5ecc1a356 h1:d3wWSjdOuGrMH
 github.com/gopherjs/gopherjs v0.0.0-20211111143520-d0d5ecc1a356/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@@ -455,8 +468,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -522,6 +536,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nacos-group/nacos-sdk-go/v2 v2.2.5 h1:r0wwT7PayEjvEHzWXwr1ROi/JSqzujM4w+1L5ikThzQ=
@@ -627,6 +643,11 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhD
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
+github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
+github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
+github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo=
 github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM=
 github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
@@ -635,8 +656,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@@ -690,8 +712,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
 github.com/swaggo/gin-swagger v1.3.3 h1:XHyYmeNVFG5PbyWHG4jXtxOm2P4kiZapDCWsyDDiQ/I=
@@ -731,6 +753,12 @@ github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52
 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY=
+github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
+github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE=
+github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE=
+github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
+github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -796,8 +824,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -808,8 +836,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
+golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
+golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -834,8 +866,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -888,8 +920,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -914,8 +946,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -990,8 +1022,8 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1003,8 +1035,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1074,8 +1106,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
-golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1156,8 +1188,8 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
 google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@@ -1183,8 +1215,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
-google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
+google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
+google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1198,8 +1230,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=