k.zhang před 4 roky
rodič
revize
6fbac97012

+ 15 - 0
.idea/duoduo.iml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="GoLibraries">
+    <option name="urls">
+      <list>
+        <option value="file://$MODULE_DIR$/../.." />
+      </list>
+    </option>
+  </component>
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/misc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/duoduo.iml" filepath="$PROJECT_DIR$/.idea/duoduo.iml" />
+    </modules>
+  </component>
+</project>

+ 29 - 0
.idea/watcherTasks.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectTasksOptions">
+    <TaskOptions isEnabled="true">
+      <option name="arguments" value="fmt $FilePath$" />
+      <option name="checkSyntaxErrors" value="true" />
+      <option name="description" />
+      <option name="exitCodeBehavior" value="ERROR" />
+      <option name="fileExtension" value="go" />
+      <option name="immediateSync" value="false" />
+      <option name="name" value="go fmt" />
+      <option name="output" value="$FilePath$" />
+      <option name="outputFilters">
+        <array />
+      </option>
+      <option name="outputFromStdout" value="false" />
+      <option name="program" value="$GoExecPath$" />
+      <option name="runOnExternalChanges" value="false" />
+      <option name="scopeName" value="Project Files" />
+      <option name="trackOnlyRoot" value="true" />
+      <option name="workingDir" value="$ProjectFileDir$" />
+      <envs>
+        <env name="GOROOT" value="$GOROOT$" />
+        <env name="GOPATH" value="$GOPATH$" />
+        <env name="PATH" value="$GoBinDirs$" />
+      </envs>
+    </TaskOptions>
+  </component>
+</project>

+ 20 - 0
apis/pdd/pdd.ddk.goods.search.go

@@ -0,0 +1,20 @@
+package pdd
+
+import (
+	"duoduo/conf"
+	"duoduo/models/pdd"
+	"fmt"
+	"github.com/gin-gonic/gin"
+)
+
+func PddDdkGoodsSearch(c *gin.Context) {
+	var inData pdd.PddDdkGoodsSearchRequest
+	confIni, errConf := conf.ConnIni()
+	if errConf != nil {
+		fmt.Println(errConf)
+	}
+
+	inData.ClientId = confIni.MustValue("pdd", "client_id")
+	inData.ClientSecret = confIni.MustValue("pdd", "client_secret")
+	//PostPdd()
+}

+ 104 - 0
apis/pdd/post.go

@@ -0,0 +1,104 @@
+package pdd
+
+import (
+	"bytes"
+	"duoduo/conf"
+	"duoduo/tools"
+	"duoduo/tools/app"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+)
+
+func PostPdd(inputObj interface{}, outputObj interface{}, function string) error {
+
+	//数据转json
+	jsonData, err := tools.JsonMarshal(inputObj)
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+
+	fmt.Print("jsonData = ", jsonData)
+
+	confIni, errConf := conf.ConnIni()
+	if errConf != nil {
+		fmt.Println(errConf)
+	}
+
+	path := confIni.MustValue("pdd", "api_http")
+	fmt.Print(path)
+	path = "https://gw-api.pinduoduo.com/api/router?type=pdd.ddk.goods.search&data_type=JSON&client_id=a5d05569c6584a50bfb6600b2d150bae&timestamp=1620461572&sign=8F2C23805C8B8A388AFB512DD3EAD727"
+
+	request, err := http.NewRequest("POST", path, nil)
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+	request.Header.Set("Content-Type", "application/json;charset=UTF-8")
+	client := http.Client{}
+	//创建客户端
+	resp, err := client.Do(request) //发送请求
+	if err != nil {
+		fmt.Printf("client.Do%v", err)
+		return err
+	}
+	respBytes, err := ioutil.ReadAll(resp.Body)
+	fmt.Print(string(respBytes))
+
+	return nil
+
+}
+
+//调用阿里接口
+func Post(inputObj interface{}, outputObj interface{}, function string) error {
+	//加载配置
+	confIni, errConf := conf.ConnIni()
+	if errConf != nil {
+		fmt.Println(errConf)
+	}
+
+	hostAdd := fmt.Sprintf("%s", confIni.MustValue("aliSet", "ali_host"))
+	fmt.Println(hostAdd)
+
+	//数据转json
+	jsonData, err := tools.JsonMarshal(inputObj)
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+	app.TispELKLogs(function, "POST", jsonData)
+
+	fmt.Print("json = ", jsonData)
+
+	request, err := http.NewRequest("POST", hostAdd, bytes.NewBuffer([]byte(jsonData)))
+	if err != nil {
+		fmt.Println(err.Error())
+		return err
+	}
+	request.Header.Set("Content-Type", "application/json;charset=UTF-8")
+	client := http.Client{}
+	//创建客户端
+	resp, err := client.Do(request) //发送请求
+	if err != nil {
+		fmt.Printf("client.Do%v", err)
+		return err
+	}
+	respBytes, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		fmt.Printf("ioutil.ReadAll%v", err)
+		return err
+	}
+	fmt.Println(string(respBytes))
+	err = tools.JsonUnmarshal(string(respBytes), outputObj)
+	if err != nil {
+		return err
+	}
+	app.TispELKOutDataLogs(function, "POST", string(respBytes))
+	return nil
+
+	////byte数组直接转成string,优化内存
+	//str := (*string)(unsafe.Pointer(&respBytes))
+
+}

+ 22 - 0
apis/pdd/post_test.go

@@ -0,0 +1,22 @@
+package pdd
+
+import (
+	"duoduo/conf"
+	"duoduo/models/pdd"
+	"fmt"
+	"testing"
+)
+
+func TestPost(t *testing.T) {
+	var inData pdd.PddDdkGoodsSearchRequest
+	var outData pdd.PddDdkGoodsSearchResponse
+	confIni, errConf := conf.ConnIni()
+	if errConf != nil {
+		fmt.Println(errConf)
+	}
+
+	inData.ClientId = confIni.MustValue("pdd", "client_id")
+	inData.ClientSecret = confIni.MustValue("pdd", "client_secret")
+	inData.Type = "pdd.ddk.goods.search"
+	PostPdd(inData, outData, "pdd.ddk.goods.search")
+}

+ 5 - 0
conf/conf.ini

@@ -0,0 +1,5 @@
+[pdd]
+client_id = a5d05569c6584a50bfb6600b2d150bae
+client_secret = 1d4d5dd30f665c0ed3b315275f81fe358c3da609
+api_http = https://gw-api.pinduoduo.com/api/router
+

+ 0 - 0
conf/conf_axin.ini


+ 0 - 0
conf/conf_master.ini


+ 0 - 0
conf/conf_test.ini


+ 58 - 0
conf/conn_ini_.go

@@ -0,0 +1,58 @@
+package conf
+
+import (
+	"errors"
+	"fmt"
+	"github.com/Unknwon/goconfig"
+	"os"
+	"strings"
+)
+
+// 读取配置文件
+var connIni *goconfig.ConfigFile
+var connIniTest = false
+
+func ConnIni() (conf *goconfig.ConfigFile, err error) {
+
+	// 通过全局变量设置配置文件的名字
+	var confName string
+
+	switch strings.ToLower(os.Getenv("model")) {
+
+	case "test":
+		confName = "conf_test.ini"
+		break
+
+	case "master":
+		confName = "conf_master.ini"
+		break
+	case "axin":
+		confName = "conf_axin.ini"
+
+	default:
+		confName = "conf.ini"
+	}
+
+	fmt.Printf("load config : %s\n", confName)
+
+	// 判断是否加载成功过
+	if connIniTest == false {
+
+		var conFiles = []string{"conf/", "/conf/", "/app/conf/", "../conf/", "../../conf/", "../../../conf/", "../../../../conf/", "../../../../../conf/"}
+
+		for _, aConFile := range conFiles {
+			conn, err := goconfig.LoadConfigFile(aConFile + confName)
+			if err == nil {
+
+				connIniTest = true
+				connIni = conn
+				return connIni, nil
+
+			}
+		}
+
+		return connIni, errors.New("无法找到配置文件")
+	}
+
+	return connIni, nil
+}

+ 17 - 0
conf/conn_ini__test.go

@@ -0,0 +1,17 @@
+package conf
+
+import (
+	"testing"
+)
+
+// 读取配置文件
+
+func Test_ConnIni(t *testing.T) {
+
+	conf, err := ConnIni()
+	if err != nil {
+		t.Error(err)
+	} else {
+		t.Log(conf.MustValue("ubrSet", "ubr_host"))
+	}
+}

+ 20 - 0
go.mod

@@ -1,3 +1,23 @@
 module duoduo
 
 go 1.14
+
+require (
+	github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 // indirect
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
+	github.com/gin-gonic/gin v1.7.1 // indirect
+	github.com/go-playground/validator/v10 v10.5.0 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/json-iterator/go v1.1.11 // indirect
+	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.1 // indirect
+	github.com/olivere/elastic/v7 v7.0.4 // indirect
+	github.com/sirupsen/logrus v1.4.2 // indirect
+	github.com/ugorji/go v1.2.5 // indirect
+	golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
+	golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect
+	golang.org/x/text v0.3.6 // indirect
+	gopkg.in/sohlich/elogrus.v7 v7.0.0 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+)

+ 192 - 0
go.sum

@@ -0,0 +1,192 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8 h1:1TrMV1HmBApBbM+Hy7RCKZD6UlYWYIPPfoeXomG7+zE=
+github.com/Unknwon/goconfig v0.0.0-20200908083735-df7de6a44db8/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/aws/aws-sdk-go v1.19.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8=
+github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
+github.com/go-playground/validator/v10 v10.5.0 h1:X9rflw/KmpACwT8zdrm1upefpvdy6ur8d1kWyq6sg3E=
+github.com/go-playground/validator/v10 v10.5.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+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/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/olivere/elastic/v7 v7.0.4 h1:gyVBKOQ8RFG+jbNYqtVuYvK5jEtpj/pjpFaLrQuwA/w=
+github.com/olivere/elastic/v7 v7.0.4/go.mod h1:l4YWa59iTCcOJQXI5ZtxVjcd3p5U8GCxVgvzHZqGn3o=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc=
+github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis=
+github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+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=
+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=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+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=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs=
+golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+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/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+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=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/sohlich/elogrus.v7 v7.0.0 h1:w4pw1DTXK/bqliKbcJk7hSXKrMM/jPdvpaROC9WSK+8=
+gopkg.in/sohlich/elogrus.v7 v7.0.0/go.mod h1:nGmb0kLyAPGwIHLpHNlMlz0l0OfSaFatKgfpuO/+fnY=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 6 - 2
main.go

@@ -1,7 +1,11 @@
 package main
 
-import "fmt"
+import (
+	"duoduo/routers"
+	"fmt"
+)
 
 func main()  {
-	fmt.Println("hello duoduo!")
+	fmt.Println("hello duo!")
+	routers.InitRouter()
 }

+ 78 - 0
models/pdd/pdd.ddk.goods.search.go

@@ -0,0 +1,78 @@
+package pdd
+
+type PddDdkGoodsSearchRequest struct {
+	ClientId     string `json:"client_id"`
+	ClientSecret string `json:"client_secret"`
+	Type         string `json:"type"`
+}
+
+type PddDdkGoodsSearchResponse struct {
+	GoodsSearchResponse GoodsSearchResponse `json:"goods_search_response"`
+}
+type GoodsList struct {
+	ActivityPromotionRate       int      `json:"activity_promotion_rate"`
+	ActivityTags                []int    `json:"activity_tags"`
+	ActivityType                int      `json:"activity_type"`
+	BrandName                   string   `json:"brand_name"`
+	CatIds                      []int    `json:"cat_ids"`
+	CltCpnBatchSn               string   `json:"clt_cpn_batch_sn"`
+	CltCpnDiscount              int      `json:"clt_cpn_discount"`
+	CltCpnEndTime               int      `json:"clt_cpn_end_time"`
+	CltCpnMinAmt                int      `json:"clt_cpn_min_amt"`
+	CltCpnQuantity              int      `json:"clt_cpn_quantity"`
+	CltCpnRemainQuantity        int      `json:"clt_cpn_remain_quantity"`
+	CltCpnStartTime             int      `json:"clt_cpn_start_time"`
+	CouponDiscount              int      `json:"coupon_discount"`
+	CouponEndTime               int      `json:"coupon_end_time"`
+	CouponMinOrderAmount        int      `json:"coupon_min_order_amount"`
+	CouponRemainQuantity        int      `json:"coupon_remain_quantity"`
+	CouponStartTime             int      `json:"coupon_start_time"`
+	CouponTotalQuantity         int      `json:"coupon_total_quantity"`
+	CreateAt                    int      `json:"create_at"`
+	DescTxt                     string   `json:"desc_txt"`
+	ExtraCouponAmount           int      `json:"extra_coupon_amount"`
+	GoodsDesc                   string   `json:"goods_desc"`
+	GoodsImageURL               string   `json:"goods_image_url"`
+	GoodsLabels                 []int    `json:"goods_labels"`
+	GoodsName                   string   `json:"goods_name"`
+	GoodsSign                   string   `json:"goods_sign"`
+	GoodsThumbnailURL           string   `json:"goods_thumbnail_url"`
+	HasCoupon                   bool     `json:"has_coupon"`
+	HasMallCoupon               bool     `json:"has_mall_coupon"`
+	LgstTxt                     string   `json:"lgst_txt"`
+	MallCouponDiscountPct       int      `json:"mall_coupon_discount_pct"`
+	MallCouponEndTime           int      `json:"mall_coupon_end_time"`
+	MallCouponID                int      `json:"mall_coupon_id"`
+	MallCouponMaxDiscountAmount int      `json:"mall_coupon_max_discount_amount"`
+	MallCouponMinOrderAmount    int      `json:"mall_coupon_min_order_amount"`
+	MallCouponRemainQuantity    int      `json:"mall_coupon_remain_quantity"`
+	MallCouponStartTime         int      `json:"mall_coupon_start_time"`
+	MallCouponTotalQuantity     int      `json:"mall_coupon_total_quantity"`
+	MallCps                     int      `json:"mall_cps"`
+	MallID                      int      `json:"mall_id"`
+	MallName                    string   `json:"mall_name"`
+	MerchantType                int      `json:"merchant_type"`
+	MinGroupPrice               int      `json:"min_group_price"`
+	MinNormalPrice              int      `json:"min_normal_price"`
+	OnlySceneAuth               bool     `json:"only_scene_auth"`
+	OptID                       int      `json:"opt_id"`
+	OptIds                      []int    `json:"opt_ids"`
+	OptName                     string   `json:"opt_name"`
+	PlanType                    int      `json:"plan_type"`
+	PredictPromotionRate        int      `json:"predict_promotion_rate"`
+	PromotionRate               int      `json:"promotion_rate"`
+	SalesTip                    string   `json:"sales_tip"`
+	SearchID                    string   `json:"search_id"`
+	ServTxt                     string   `json:"serv_txt"`
+	ServiceTags                 []int    `json:"service_tags"`
+	ShareRate                   int      `json:"share_rate"`
+	SubsidyAmount               int      `json:"subsidy_amount"`
+	UnifiedTags                 []string `json:"unified_tags"`
+	ZsDuoID                     int      `json:"zs_duo_id"`
+}
+type GoodsSearchResponse struct {
+	GoodsList  []GoodsList `json:"goods_list"`
+	ListID     string      `json:"list_id"`
+	SearchID   string      `json:"search_id"`
+	TotalCount int         `json:"total_count"`
+}

+ 15 - 0
routers/router.go

@@ -0,0 +1,15 @@
+package routers
+
+import (
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+func InitRouter() {
+
+	r := gin.New()
+	r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "hello!") })
+
+	_ = r.Run(":8080")
+
+}

+ 62 - 0
tools/aes.go

@@ -0,0 +1,62 @@
+package tools
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"errors"
+)
+
+// AES 加密
+func AesEncrypt(orig string, _Aeskey string) (string, error) {
+
+	origData := []byte(orig)
+	key := []byte(_Aeskey)
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	blockSize := block.BlockSize()
+	origData = func(ciphertext []byte, blockSize int) []byte {
+		padding := blockSize - len(ciphertext)%blockSize
+		padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+		return append(ciphertext, padtext...)
+	}(origData, blockSize)
+	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
+	crypted := make([]byte, len(origData))
+	blockMode.CryptBlocks(crypted, origData)
+	return base64.StdEncoding.EncodeToString(crypted), nil
+}
+
+// AES 解密
+func AesDecrypt(cryted string, _Aeskey string) (string, error) {
+
+	if cryted == "" {
+		return "", errors.New("错误的数据")
+	}
+
+	crypted, err := base64.StdEncoding.DecodeString(cryted)
+	if err != nil {
+		return "", err
+	}
+	key := []byte(_Aeskey)
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return "", err
+	}
+
+	blockSize := block.BlockSize()
+	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
+	origData := make([]byte, len(crypted))
+	blockMode.CryptBlocks(origData, crypted)
+	origData = func(origData []byte) []byte {
+		length := len(origData)
+		unpadding := int(origData[length-1])
+		return origData[:(length - unpadding)]
+	}(origData)
+	return string(origData[:]), nil
+}

+ 137 - 0
tools/app/elk.go

@@ -0,0 +1,137 @@
+package app
+
+import (
+	"duoduo/conf"
+	"duoduo/tools"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/olivere/elastic/v7"
+	"github.com/sirupsen/logrus"
+	"gopkg.in/sohlich/elogrus.v7"
+	"log"
+)
+
+var logs *logrus.Logger
+
+func InitLogElk() {
+	fmt.Println("InitLogElk")
+	//加载配置
+	confIni, errConf := conf.ConnIni()
+	if errConf != nil {
+
+		fmt.Println(errConf)
+	}
+
+	url := confIni.MustValue("elk", "url")
+	host := confIni.MustValue("elk", "host")
+	index := confIni.MustValue("elk", "index")
+
+	logs = logrus.New()
+	fmt.Println("url = ", url)
+	client, err := elastic.NewSimpleClient(elastic.SetURL(url))
+	if err != nil {
+		fmt.Println("err = ", err.Error())
+		log.Panic(err)
+	}
+	hook, err := elogrus.NewAsyncElasticHook(client, host, logrus.InfoLevel, index)
+	if err != nil {
+		fmt.Println("err = ", err.Error())
+		log.Panic(err)
+	}
+	logs.Hooks.Add(hook)
+	logs.WithFields(logrus.Fields{
+		"name": "joe",
+		"age":  42,
+	}).Error("elk start!")
+
+}
+
+func GetLog() *logrus.Logger {
+	if logs == nil {
+		return logrus.New()
+	}
+	return logs
+}
+
+func ElkError(msg interface{}) {
+	//log.Error(msg)
+}
+
+func ELKLogs(c *gin.Context, json string) {
+
+	logs.WithFields(logrus.Fields{
+		"Url":    c.Request.RequestURI,
+		"Status": c.Writer.Status(),
+		"Method": c.Request.Method,
+	}).Infof("请求参数:%s", json)
+
+}
+
+func OrderELKLogs(url string, method string, orderId string, json interface{}) {
+	jsonStr, err := tools.JsonMarshal(json)
+	if err != nil {
+		logs.WithFields(logrus.Fields{
+			"Url":     url,
+			"Method":  method,
+			"OrderId": orderId,
+		}).Error("错误说明:结构体转json失败 %s", err.Error())
+		return
+	}
+
+	logs.WithFields(logrus.Fields{
+		"Url":     url,
+		"Method":  method,
+		"OrderId": orderId,
+	}).Infof("请求参数:%s", jsonStr)
+
+}
+
+func FromDataELKLogs(url string, method string, orderId string, data string) {
+
+	logs.WithFields(logrus.Fields{
+		"Url":     url,
+		"Method":  method,
+		"OrderId": orderId,
+	}).Infof("请求参数:%s", data)
+
+}
+
+func OrderOutDataELKLogs(url string, method string, orderId string, json interface{}) {
+	jsonStr, err := tools.JsonMarshal(json)
+	if err != nil {
+		logs.WithFields(logrus.Fields{
+			"Url":     url,
+			"Method":  method,
+			"OrderId": orderId,
+		}).Error("返回错误说明:结构体转json失败 %s", err.Error())
+		return
+	}
+
+	logs.WithFields(logrus.Fields{
+		"Url":     url,
+		"Method":  method,
+		"OrderId": orderId,
+	}).Infof("返回参数:%s", jsonStr)
+
+}
+
+func TispELKLogs(url string, method string, json string) {
+	logs.WithFields(logrus.Fields{
+		"Url":    url,
+		"Method": method,
+	}).Infof("请求参数:%s", json)
+}
+
+func TispELKOutDataLogs(url string, method string, json string) {
+	logs.WithFields(logrus.Fields{
+		"Url":    url,
+		"Method": method,
+	}).Infof("返回参数:%s", json)
+}
+
+func TispELKOutDataErrorLogs(url string, method string, json string) {
+	logs.WithFields(logrus.Fields{
+		"Url":    url,
+		"Method": method,
+	}).Error("错误说明:%s", json)
+}

+ 74 - 0
tools/app/message.go

@@ -0,0 +1,74 @@
+package app
+
+//错误码描述
+const (
+	Success                      = "成功" // 200
+	Fail                         = "Fail"
+	ServerError                  = "Server Error" //500
+	ClientError                  = "Client Error" //400
+	DatabaseError                = "Sql Error"
+	RequestParamError            = "request parameters error"
+	DataBaseError                = "网络异常请稍后重试"
+	InputError                   = "参数解析错误"
+	UserIdError                  = "用户ID不能为空"
+	OriginalTransaction          = "原交易信息错误"
+	ExtOrderIdAgain              = "本订单已经发起了订单退款"
+	RefundUTourStatusAwaitTicket = "正在出票中,暂时不允许退款,请稍后再试。"
+	TicketUsage                  = "门票订单中存在已使用门票,不允许改期升级。"
+	UserPayError                 = "未支付或退款状态,不允许改期升级。"
+	UTourStatusNotEqualTicket    = "只有已出票的订单允许改期升级"
+	JsonUnmarshalError           = "json数据解析失败"
+	JsonMarshalError             = "数据转换json失败"
+	RescheduleCreateError        = "改期升级订单创建失败"
+	RescheduleError              = "改期升级订单失败"
+	PushRegisterError            = "推送注册失败"
+)
+
+const (
+	// 通知分组类型
+	GroupShare            = "share"            // 浏览/感兴趣
+	GroupOrder            = "order"            // 订单通知
+	GroupOrderTransaction = "orderTransaction" // 订单事务
+
+	// 消息类型
+	GroupShareMsgTypeBrowse                   = "browse"        // 浏览
+	GroupShareMsgTypeInterested               = "interested"    // 感兴趣
+	GroupOrderMsgTypeCreate                   = "create"        // 创建订单
+	GroupOrderMsgTypeReject                   = "reject"        // 创建订单
+	GroupOrderMsgTypeCancel                   = "cancel"        // 取消订单
+	GroupOrderMsgTypeAffirm                   = "affirm"        // 确认订单
+	GroupOrderMsgTypePending                  = "pending"       // 待支付
+	GroupOrderMsgTypePaid                     = "paid"          // 已支付
+	GroupOrderTransactionMsgTypeLeave         = "leave"         // 退团
+	GroupOrderTransactionMsgTypeNegotiate     = "negotiate"     // 议价
+	GroupOrderTransactionMsgTypeChangeTourist = "changeTourist" // 变更游
+	GroupOrderMsgTypeFinalPay                 = "finalPay"      // 尾款支付
+	GroupOrderMsgTypeRefunded                 = "refunded"      // 已退款
+	GroupOrderMsgDepartureNotice              = "departureNotice"
+)
+
+//退款-改期交易类型
+
+//订单表,众信订单状态
+const (
+	UTourStatusAwaitPay       = "1"  //待付款	1  创建订单
+	UTourStatusAwaitTicket    = "2"  //待出票	2  创建订单后完成支付(发起支付回调成功后修改成待出票状态)
+	UTourStatusTicket         = "3"  //已出票	3
+	UTourStatusCancel         = "4"  //已取消	4  异常情况下进行取消
+	UTourStatusRefunding      = "5"  //退款中	5
+	UTourStatusRefundComplete = "6"  //退款完成	6
+	UTourStatusRefundFail     = "7"  //退款失败	7
+	UTourStatusChangeAwaitPay = "8"  //(改签)待付款	8
+	UTourStatusResigning      = "9"  //改签中	9
+	UTourStatusChangeComplete = "10" //已改签	10
+	UTourStatusChangeFail     = "11" //改签失败	11
+	UTourStatusUpgradeFail    = "12" //升级失败	12
+	UTourStatusShutDown       = "13" //已关闭	13
+)
+
+//订单表 支付状态
+const (
+	UserPayStatusUnPay  = "1" //未支付
+	UserPayStatusPay    = "2" //已支付
+	UserPayStatusRefund = "3" //退款
+)

+ 61 - 0
tools/app/model.go

@@ -0,0 +1,61 @@
+package app
+
+type Response struct {
+	ApiBaseOutputHead ApiBaseOutputHead `json:"apiBaseOutputHead"`
+	// 数据集
+	Data interface{} `json:"data"`
+}
+
+type AliResponse struct {
+	Code    int    `json:"code"`
+	Message string `json:"message"`
+	// 数据集
+	Data interface{} `json:"data"`
+}
+type ApiBaseOutputHead struct {
+	Code int    `json:"code" example:"200"`
+	Msg  string `json:"msg"`
+}
+
+type Page struct {
+	List      interface{} `json:"rows"`
+	Count     int         `json:"count"`
+	PageIndex int         `json:"pageIndex"`
+	PageSize  int         `json:"pageSize"`
+}
+
+type PageResponse struct {
+	ApiBaseOutputHead ApiBaseOutputHead `json:"apiBaseOutputHead"`
+	//Data              interface{}       `json:"data"`
+	// 代码
+	//Code int `json:"code" example:"200"`
+	// 数据集
+	Data Page `json:"data"`
+	// 消息
+	//Msg string `json:"msg"`
+}
+
+type PageRes struct {
+
+	// 代码
+	Code int `json:"code"`
+	// 数据集
+	Data Page `json:"data"`
+	// 消息
+	Msg string `json:"message"`
+}
+
+func (res *Response) ReturnOK() *Response {
+	res.ApiBaseOutputHead.Code = 200
+	return res
+}
+
+func (res *Response) ReturnError(code int) *Response {
+	res.ApiBaseOutputHead.Code = code
+	return res
+}
+
+func (res *PageResponse) ReturnOK() *PageResponse {
+	res.ApiBaseOutputHead.Code = 200
+	return res
+}

+ 70 - 0
tools/app/return.go

@@ -0,0 +1,70 @@
+package app
+
+import (
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+//func SetApiBaseOutputHead(code int, message string) models.ApiBaseOutputHead {
+//	var outData ali.AliBaseResponse
+//	outData.Code = code
+//	outData.Msg = message
+//	return outData
+//}
+
+//func ClientErr(c *gin.Context, msg string) {
+//	var aliOut ali.AliBaseResponse
+//	aliOut.Code = 400
+//	aliOut.Msg = msg
+//	c.JSON(http.StatusOK, aliOut)
+//}
+//
+//func ServerErr(c *gin.Context, msg string) {
+//	var aliOut ali.AliBaseResponse
+//	aliOut.Code = 500
+//	aliOut.Msg = msg
+//	c.JSON(http.StatusOK, aliOut)
+//}
+
+// 失败数据处理
+func Error(c *gin.Context, code int, err error, msg string) {
+	var res Response
+	if err != nil {
+		res.ApiBaseOutputHead.Msg = err.Error()
+	} else {
+		if msg != "" {
+			res.ApiBaseOutputHead.Msg = msg
+		}
+	}
+	c.JSON(http.StatusOK, res.ReturnError(code))
+}
+
+// 通常成功数据处理
+func OK(c *gin.Context, data interface{}, msg string) {
+	var res AliResponse
+	res.Data = data
+	if msg != "" {
+		res.Message = msg
+	}
+	res.Code = 200
+	c.JSON(http.StatusOK, res)
+}
+
+// 分页数据处理
+func PageOK(c *gin.Context, result interface{}, count int, pageIndex int, pageSize int, msg string) {
+	var res PageRes
+	res.Data.List = result
+	res.Data.Count = count
+	res.Data.PageIndex = pageIndex
+	res.Data.PageSize = pageSize
+	if msg != "" {
+		res.Msg = msg
+	}
+	res.Code = 200
+	c.JSON(http.StatusOK, res)
+}
+
+// 兼容函数
+func Custum(c *gin.Context, data gin.H) {
+	c.JSON(http.StatusOK, data)
+}

+ 36 - 0
tools/json.go

@@ -0,0 +1,36 @@
+package tools
+
+import (
+	"encoding/json"
+)
+
+// 获得一个对象的Key,Value值
+func JsonMarshal(v interface{}) (jsonStr string, err error) {
+
+	jsonBytes, err := json.Marshal(v)
+	if err != nil {
+
+		return "", err
+	} else {
+
+		return string(jsonBytes), nil
+	}
+
+}
+
+// 对象转换为Json,并且格式化清晰的样子
+func JsonMarshalFormat(v interface{}) (jsonStr string, err error) {
+
+	jsonBytes, err := json.MarshalIndent(v, "", "    ")
+	if err != nil {
+		return "", err
+	} else {
+		return string(jsonBytes), nil
+	}
+
+}
+
+// JSON反序列化
+func JsonUnmarshal(jsonStr string, v interface{}) error {
+	return json.Unmarshal([]byte(jsonStr), v)
+}

+ 784 - 0
tools/jwtauth.go

@@ -0,0 +1,784 @@
+package tools
+
+import (
+	"crypto/rsa"
+	"errors"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/dgrijalva/jwt-go"
+	"github.com/gin-gonic/gin"
+)
+
+// MapClaims type that uses the map[string]interface{} for JSON decoding
+// This is the default claims type if you don't supply one
+type MapClaims map[string]interface{}
+
+// GinJWTMiddleware provides a Json-Web-Token authentication implementation. On failure, a 401 HTTP response
+// is returned. On success, the wrapped middleware is called, and the userID is made available as
+// c.Get("userID").(string).
+// Users can get a token by posting a json request to LoginHandler. The token then needs to be passed in
+// the Authentication header. Example: Authorization:Bearer XXX_TOKEN_XXX
+type GinJWTMiddleware struct {
+	// Realm name to display to the user. Required.
+	Realm string
+
+	// signing algorithm - possible values are HS256, HS384, HS512, RS256, RS384 or RS512
+	// Optional, default is HS256.
+	SigningAlgorithm string
+
+	// Secret key used for signing. Required.
+	Key []byte
+
+	// Duration that a jwt token is valid. Optional, defaults to one hour.
+	Timeout time.Duration
+
+	// This field allows clients to refresh their token until MaxRefresh has passed.
+	// Note that clients can refresh their token in the last moment of MaxRefresh.
+	// This means that the maximum validity timespan for a token is TokenTime + MaxRefresh.
+	// Optional, defaults to 0 meaning not refreshable.
+	MaxRefresh time.Duration
+
+	// Callback function that should perform the authentication of the user based on login info.
+	// Must return user data as user identifier, it will be stored in Claim Array. Required.
+	// Check error (e) to determine the appropriate error message.
+	Authenticator func(c *gin.Context) (interface{}, error)
+
+	// Callback function that should perform the authorization of the authenticated user. Called
+	// only after an authentication success. Must return true on success, false on failure.
+	// Optional, default to success.
+	Authorizator func(data interface{}, c *gin.Context) bool
+
+	// Callback function that will be called during login.
+	// Using this function it is possible to add additional payload data to the webtoken.
+	// The data is then made available during requests via c.Get("JWT_PAYLOAD").
+	// Note that the payload is not encrypted.
+	// The attributes mentioned on jwt.io can't be used as keys for the map.
+	// Optional, by default no additional data will be set.
+	PayloadFunc func(data interface{}) MapClaims
+
+	// User can define own Unauthorized func.
+	Unauthorized func(*gin.Context, int, string)
+
+	// User can define own LoginResponse func.
+	LoginResponse func(*gin.Context, int, string, time.Time)
+
+	// User can define own LogoutResponse func.
+	LogoutResponse func(*gin.Context, int)
+
+	// User can define own RefreshResponse func.
+	RefreshResponse func(*gin.Context, int, string, time.Time)
+
+	// Set the identity handler function
+	IdentityHandler func(*gin.Context) interface{}
+
+	// Set the identity key
+	IdentityKey string
+
+	// TokenLookup is a string in the form of "<source>:<name>" that is used
+	// to extract token from the request.
+	// Optional. Default value "header:Authorization".
+	// Possible values:
+	// - "header:<name>"
+	// - "query:<name>"
+	// - "cookie:<name>"
+	TokenLookup string
+
+	// TokenHeadName is a string in the header. Default value is "Bearer"
+	TokenHeadName string
+
+	// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
+	TimeFunc func() time.Time
+
+	// HTTP Status messages for when something in the JWT middleware fails.
+	// Check error (e) to determine the appropriate error message.
+	HTTPStatusMessageFunc func(e error, c *gin.Context) string
+
+	// Private key file for asymmetric algorithms
+	PrivKeyFile string
+
+	// Public key file for asymmetric algorithms
+	PubKeyFile string
+
+	// Private key
+	privKey *rsa.PrivateKey
+
+	// Public key
+	pubKey *rsa.PublicKey
+
+	// Optionally return the token as a cookie
+	SendCookie bool
+
+	// Duration that a cookie is valid. Optional, by default equals to Timeout value.
+	CookieMaxAge time.Duration
+
+	// Allow insecure cookies for development over http
+	SecureCookie bool
+
+	// Allow cookies to be accessed client side for development
+	CookieHTTPOnly bool
+
+	// Allow cookie domain change for development
+	CookieDomain string
+
+	// SendAuthorization allow return authorization header for every request
+	SendAuthorization bool
+
+	// Disable abort() of context.
+	DisabledAbort bool
+
+	// CookieName allow cookie name change for development
+	CookieName string
+
+	// CookieSameSite allow use http.SameSite cookie param
+	CookieSameSite http.SameSite
+}
+
+var (
+	// ErrMissingSecretKey indicates Secret key is required
+	ErrMissingSecretKey = errors.New("secret key is required")
+
+	// ErrForbidden when HTTP status 403 is given
+	ErrForbidden = errors.New("you don't have permission to access this resource")
+
+	// ErrMissingAuthenticatorFunc indicates Authenticator is required
+	ErrMissingAuthenticatorFunc = errors.New("ginJWTMiddleware.Authenticator func is undefined")
+
+	// ErrMissingLoginValues indicates a user tried to authenticate without username or password
+	ErrMissingLoginValues = errors.New("missing Username or Password")
+
+	// ErrFailedAuthentication indicates authentication failed, could be faulty username or password
+	ErrFailedAuthentication = errors.New("incorrect Username or Password")
+
+	// ErrFailedTokenCreation indicates JWT Token failed to create, reason unknown
+	ErrFailedTokenCreation = errors.New("failed to create JWT Token")
+
+	// ErrExpiredToken indicates JWT token has expired. Can't refresh.
+	ErrExpiredToken = errors.New("token is expired")
+
+	// ErrEmptyAuthHeader can be thrown if authing with a HTTP header, the Auth header needs to be set
+	ErrEmptyAuthHeader = errors.New("auth header is empty")
+
+	// ErrMissingExpField missing exp field in token
+	ErrMissingExpField = errors.New("missing exp field")
+
+	// ErrWrongFormatOfExp field must be float64 format
+	ErrWrongFormatOfExp = errors.New("exp must be float64 format")
+
+	// ErrInvalidAuthHeader indicates auth header is invalid, could for example have the wrong Realm name
+	ErrInvalidAuthHeader = errors.New("auth header is invalid")
+
+	// ErrEmptyQueryToken can be thrown if authing with URL Query, the query token variable is empty
+	ErrEmptyQueryToken = errors.New("query token is empty")
+
+	// ErrEmptyCookieToken can be thrown if authing with a cookie, the token cookie is empty
+	ErrEmptyCookieToken = errors.New("cookie token is empty")
+
+	// ErrEmptyParamToken can be thrown if authing with parameter in path, the parameter in path is empty
+	ErrEmptyParamToken = errors.New("parameter token is empty")
+
+	// ErrInvalidSigningAlgorithm indicates signing algorithm is invalid, needs to be HS256, HS384, HS512, RS256, RS384 or RS512
+	ErrInvalidSigningAlgorithm = errors.New("invalid signing algorithm")
+
+	// ErrNoPrivKeyFile indicates that the given private key is unreadable
+	ErrNoPrivKeyFile = errors.New("private key file unreadable")
+
+	// ErrNoPubKeyFile indicates that the given public key is unreadable
+	ErrNoPubKeyFile = errors.New("public key file unreadable")
+
+	// ErrInvalidPrivKey indicates that the given private key is invalid
+	ErrInvalidPrivKey = errors.New("private key invalid")
+
+	// ErrInvalidPubKey indicates the the given public key is invalid
+	ErrInvalidPubKey = errors.New("public key invalid")
+
+	// IdentityKey default identity key
+	IdentityKey = "identity"
+
+	//NiceKey = "nice"
+	//
+	//DataScopeKey = "datascope"
+	//
+	//RKey      = "r"
+	//RoleIdKey = "roleid"
+	//
+	//RoleKey = "rolekey"
+	//
+	//RoleNameKey = "rolename"
+)
+
+// New for check error with GinJWTMiddleware
+func New(m *GinJWTMiddleware) (*GinJWTMiddleware, error) {
+	if err := m.MiddlewareInit(); err != nil {
+		return nil, err
+	}
+
+	return m, nil
+}
+
+func (mw *GinJWTMiddleware) readKeys() error {
+	err := mw.privateKey()
+	if err != nil {
+		return err
+	}
+	err = mw.publicKey()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (mw *GinJWTMiddleware) privateKey() error {
+	keyData, err := ioutil.ReadFile(mw.PrivKeyFile)
+	if err != nil {
+		return ErrNoPrivKeyFile
+	}
+	key, err := jwt.ParseRSAPrivateKeyFromPEM(keyData)
+	if err != nil {
+		return ErrInvalidPrivKey
+	}
+	mw.privKey = key
+	return nil
+}
+
+func (mw *GinJWTMiddleware) publicKey() error {
+	keyData, err := ioutil.ReadFile(mw.PubKeyFile)
+	if err != nil {
+		return ErrNoPubKeyFile
+	}
+	key, err := jwt.ParseRSAPublicKeyFromPEM(keyData)
+	if err != nil {
+		return ErrInvalidPubKey
+	}
+	mw.pubKey = key
+	return nil
+}
+
+func (mw *GinJWTMiddleware) usingPublicKeyAlgo() bool {
+	switch mw.SigningAlgorithm {
+	case "RS256", "RS512", "RS384":
+		return true
+	}
+	return false
+}
+
+// MiddlewareInit initialize jwt configs.
+func (mw *GinJWTMiddleware) MiddlewareInit() error {
+
+	if mw.TokenLookup == "" {
+		mw.TokenLookup = "header:Authorization"
+	}
+
+	if mw.SigningAlgorithm == "" {
+		mw.SigningAlgorithm = "HS256"
+	}
+
+	if mw.Timeout == 0 {
+		mw.Timeout = time.Hour
+	}
+
+	if mw.TimeFunc == nil {
+		mw.TimeFunc = time.Now
+	}
+
+	mw.TokenHeadName = strings.TrimSpace(mw.TokenHeadName)
+	if len(mw.TokenHeadName) == 0 {
+		mw.TokenHeadName = "Bearer"
+	}
+
+	if mw.Authorizator == nil {
+		mw.Authorizator = func(data interface{}, c *gin.Context) bool {
+			return true
+		}
+	}
+
+	if mw.Unauthorized == nil {
+		mw.Unauthorized = func(c *gin.Context, code int, message string) {
+			c.JSON(code, gin.H{
+				"code":    code,
+				"message": message,
+			})
+		}
+	}
+
+	if mw.LoginResponse == nil {
+		mw.LoginResponse = func(c *gin.Context, code int, token string, expire time.Time) {
+			c.JSON(http.StatusOK, gin.H{
+				"code":   http.StatusOK,
+				"token":  token,
+				"expire": expire.Format(time.RFC3339),
+			})
+		}
+	}
+
+	if mw.LogoutResponse == nil {
+		mw.LogoutResponse = func(c *gin.Context, code int) {
+			c.JSON(http.StatusOK, gin.H{
+				"code": http.StatusOK,
+			})
+		}
+	}
+
+	if mw.RefreshResponse == nil {
+		mw.RefreshResponse = func(c *gin.Context, code int, token string, expire time.Time) {
+			c.JSON(http.StatusOK, gin.H{
+				"code":   http.StatusOK,
+				"token":  token,
+				"expire": expire.Format(time.RFC3339),
+			})
+		}
+	}
+
+	if mw.IdentityKey == "" {
+		mw.IdentityKey = IdentityKey
+	}
+
+	if mw.IdentityHandler == nil {
+		mw.IdentityHandler = func(c *gin.Context) interface{} {
+			claims := ExtractClaims(c)
+			return claims[mw.IdentityKey]
+		}
+	}
+
+	if mw.HTTPStatusMessageFunc == nil {
+		mw.HTTPStatusMessageFunc = func(e error, c *gin.Context) string {
+			return e.Error()
+		}
+	}
+
+	if mw.Realm == "" {
+		mw.Realm = "gin jwt"
+	}
+
+	if mw.CookieMaxAge == 0 {
+		mw.CookieMaxAge = mw.Timeout
+	}
+
+	if mw.CookieName == "" {
+		mw.CookieName = "jwt"
+	}
+
+	if mw.usingPublicKeyAlgo() {
+		return mw.readKeys()
+	}
+
+	if mw.Key == nil {
+		return ErrMissingSecretKey
+	}
+	return nil
+}
+
+// MiddlewareFunc makes GinJWTMiddleware implement the Middleware interface.
+func (mw *GinJWTMiddleware) MiddlewareFunc() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		mw.middlewareImpl(c)
+	}
+}
+
+func (mw *GinJWTMiddleware) middlewareImpl(c *gin.Context) {
+	claims, err := mw.GetClaimsFromJWT(c)
+	if err != nil {
+		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
+		return
+	}
+
+	if claims["exp"] == nil {
+		mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrMissingExpField, c))
+		return
+	}
+
+	if _, ok := claims["exp"].(float64); !ok {
+		mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrWrongFormatOfExp, c))
+		return
+	}
+
+	if int64(claims["exp"].(float64)) < mw.TimeFunc().Unix() {
+		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrExpiredToken, c))
+		return
+	}
+
+	c.Set("JWT_PAYLOAD", claims)
+	identity := mw.IdentityHandler(c)
+
+	if identity != nil {
+		c.Set(mw.IdentityKey, identity)
+	}
+
+	if !mw.Authorizator(identity, c) {
+		mw.unauthorized(c, http.StatusForbidden, mw.HTTPStatusMessageFunc(ErrForbidden, c))
+		return
+	}
+
+	c.Next()
+}
+
+// GetClaimsFromJWT get claims from JWT token
+func (mw *GinJWTMiddleware) GetClaimsFromJWT(c *gin.Context) (MapClaims, error) {
+	token, err := mw.ParseToken(c)
+
+	if err != nil {
+		return nil, err
+	}
+
+	if mw.SendAuthorization {
+		if v, ok := c.Get("JWT_TOKEN"); ok {
+			c.Header("Authorization", mw.TokenHeadName+" "+v.(string))
+		}
+	}
+
+	claims := MapClaims{}
+	for key, value := range token.Claims.(jwt.MapClaims) {
+		claims[key] = value
+	}
+
+	return claims, nil
+}
+
+// LoginHandler can be used by clients to get a jwt token.
+// Payload needs to be json in the form of {"username": "USERNAME", "password": "PASSWORD"}.
+// Reply will be of the form {"token": "TOKEN"}.
+func (mw *GinJWTMiddleware) LoginHandler(c *gin.Context) {
+	if mw.Authenticator == nil {
+		mw.unauthorized(c, http.StatusInternalServerError, mw.HTTPStatusMessageFunc(ErrMissingAuthenticatorFunc, c))
+		return
+	}
+
+	data, err := mw.Authenticator(c)
+
+	if err != nil {
+		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
+		return
+	}
+
+	// Create the token
+	token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm))
+	claims := token.Claims.(jwt.MapClaims)
+
+	if mw.PayloadFunc != nil {
+		for key, value := range mw.PayloadFunc(data) {
+			claims[key] = value
+		}
+	}
+
+	expire := mw.TimeFunc().Add(mw.Timeout)
+	claims["exp"] = expire.Unix()
+	claims["orig_iat"] = mw.TimeFunc().Unix()
+	tokenString, err := mw.signedString(token)
+
+	if err != nil {
+		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrFailedTokenCreation, c))
+		return
+	}
+
+	// set cookie
+	if mw.SendCookie {
+		expireCookie := mw.TimeFunc().Add(mw.CookieMaxAge)
+		maxage := int(expireCookie.Unix() - mw.TimeFunc().Unix())
+
+		if mw.CookieSameSite != 0 {
+			c.SetSameSite(mw.CookieSameSite)
+		}
+
+		c.SetCookie(
+			mw.CookieName,
+			tokenString,
+			maxage,
+			"/",
+			mw.CookieDomain,
+			mw.SecureCookie,
+			mw.CookieHTTPOnly,
+		)
+	}
+
+	mw.LoginResponse(c, http.StatusOK, tokenString, expire)
+}
+
+// LogoutHandler can be used by clients to remove the jwt cookie (if set)
+func (mw *GinJWTMiddleware) LogoutHandler(c *gin.Context) {
+	// delete auth cookie
+	if mw.SendCookie {
+		if mw.CookieSameSite != 0 {
+			c.SetSameSite(mw.CookieSameSite)
+		}
+
+		c.SetCookie(
+			mw.CookieName,
+			"",
+			-1,
+			"/",
+			mw.CookieDomain,
+			mw.SecureCookie,
+			mw.CookieHTTPOnly,
+		)
+	}
+
+	mw.LogoutResponse(c, http.StatusOK)
+}
+
+func (mw *GinJWTMiddleware) signedString(token *jwt.Token) (string, error) {
+	var tokenString string
+	var err error
+	if mw.usingPublicKeyAlgo() {
+		tokenString, err = token.SignedString(mw.privKey)
+	} else {
+		tokenString, err = token.SignedString(mw.Key)
+	}
+	return tokenString, err
+}
+
+// RefreshHandler can be used to refresh a token. The token still needs to be valid on refresh.
+// Shall be put under an endpoint that is using the GinJWTMiddleware.
+// Reply will be of the form {"token": "TOKEN"}.
+func (mw *GinJWTMiddleware) RefreshHandler(c *gin.Context) {
+	tokenString, expire, err := mw.RefreshToken(c)
+	if err != nil {
+		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
+		return
+	}
+
+	mw.RefreshResponse(c, http.StatusOK, tokenString, expire)
+}
+
+// RefreshToken refresh token and check if token is expired
+func (mw *GinJWTMiddleware) RefreshToken(c *gin.Context) (string, time.Time, error) {
+	claims, err := mw.CheckIfTokenExpire(c)
+	if err != nil {
+		return "", time.Now(), err
+	}
+
+	// Create the token
+	newToken := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm))
+	newClaims := newToken.Claims.(jwt.MapClaims)
+
+	for key := range claims {
+		newClaims[key] = claims[key]
+	}
+
+	expire := mw.TimeFunc().Add(mw.Timeout)
+	newClaims["exp"] = expire.Unix()
+	newClaims["orig_iat"] = mw.TimeFunc().Unix()
+	tokenString, err := mw.signedString(newToken)
+
+	if err != nil {
+		return "", time.Now(), err
+	}
+
+	// set cookie
+	if mw.SendCookie {
+		expireCookie := mw.TimeFunc().Add(mw.CookieMaxAge)
+		maxage := int(expireCookie.Unix() - time.Now().Unix())
+
+		if mw.CookieSameSite != 0 {
+			c.SetSameSite(mw.CookieSameSite)
+		}
+
+		c.SetCookie(
+			mw.CookieName,
+			tokenString,
+			maxage,
+			"/",
+			mw.CookieDomain,
+			mw.SecureCookie,
+			mw.CookieHTTPOnly,
+		)
+	}
+
+	return tokenString, expire, nil
+}
+
+// CheckIfTokenExpire check if token expire
+func (mw *GinJWTMiddleware) CheckIfTokenExpire(c *gin.Context) (jwt.MapClaims, error) {
+	token, err := mw.ParseToken(c)
+
+	if err != nil {
+		// If we receive an error, and the error is anything other than a single
+		// ValidationErrorExpired, we want to return the error.
+		// If the error is just ValidationErrorExpired, we want to continue, as we can still
+		// refresh the token if it's within the MaxRefresh time.
+		// (see https://github.com/appleboy/gin-jwt/issues/176)
+		validationErr, ok := err.(*jwt.ValidationError)
+		if !ok || validationErr.Errors != jwt.ValidationErrorExpired {
+			return nil, err
+		}
+	}
+
+	claims := token.Claims.(jwt.MapClaims)
+
+	origIat := int64(claims["orig_iat"].(float64))
+
+	if origIat < mw.TimeFunc().Add(-mw.MaxRefresh).Unix() {
+		return nil, ErrExpiredToken
+	}
+
+	return claims, nil
+}
+
+// TokenGenerator method that clients can use to get a jwt token.
+func (mw *GinJWTMiddleware) TokenGenerator(data interface{}) (string, time.Time, error) {
+	token := jwt.New(jwt.GetSigningMethod(mw.SigningAlgorithm))
+	claims := token.Claims.(jwt.MapClaims)
+
+	if mw.PayloadFunc != nil {
+		for key, value := range mw.PayloadFunc(data) {
+			claims[key] = value
+		}
+	}
+
+	expire := mw.TimeFunc().UTC().Add(mw.Timeout)
+	claims["exp"] = expire.Unix()
+	claims["orig_iat"] = mw.TimeFunc().Unix()
+	tokenString, err := mw.signedString(token)
+	if err != nil {
+		return "", time.Time{}, err
+	}
+
+	return tokenString, expire, nil
+}
+
+func (mw *GinJWTMiddleware) jwtFromHeader(c *gin.Context, key string) (string, error) {
+	authHeader := c.Request.Header.Get(key)
+
+	if authHeader == "" {
+		return "", ErrEmptyAuthHeader
+	}
+
+	parts := strings.SplitN(authHeader, " ", 2)
+	if !(len(parts) == 2 && parts[0] == mw.TokenHeadName) {
+		return "", ErrInvalidAuthHeader
+	}
+
+	return parts[1], nil
+}
+
+func (mw *GinJWTMiddleware) jwtFromQuery(c *gin.Context, key string) (string, error) {
+	token := c.Query(key)
+
+	if token == "" {
+		return "", ErrEmptyQueryToken
+	}
+
+	return token, nil
+}
+
+func (mw *GinJWTMiddleware) jwtFromCookie(c *gin.Context, key string) (string, error) {
+	cookie, _ := c.Cookie(key)
+
+	if cookie == "" {
+		return "", ErrEmptyCookieToken
+	}
+
+	return cookie, nil
+}
+
+func (mw *GinJWTMiddleware) jwtFromParam(c *gin.Context, key string) (string, error) {
+	token := c.Param(key)
+
+	if token == "" {
+		return "", ErrEmptyParamToken
+	}
+
+	return token, nil
+}
+
+// ParseToken parse jwt token from gin context
+func (mw *GinJWTMiddleware) ParseToken(c *gin.Context) (*jwt.Token, error) {
+	var token string
+	var err error
+
+	methods := strings.Split(mw.TokenLookup, ",")
+	for _, method := range methods {
+		if len(token) > 0 {
+			break
+		}
+		parts := strings.Split(strings.TrimSpace(method), ":")
+		k := strings.TrimSpace(parts[0])
+		v := strings.TrimSpace(parts[1])
+		switch k {
+		case "header":
+			token, err = mw.jwtFromHeader(c, v)
+		case "query":
+			token, err = mw.jwtFromQuery(c, v)
+		case "cookie":
+			token, err = mw.jwtFromCookie(c, v)
+		case "param":
+			token, err = mw.jwtFromParam(c, v)
+		}
+	}
+
+	if err != nil {
+		return nil, err
+	}
+
+	return jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
+		if jwt.GetSigningMethod(mw.SigningAlgorithm) != t.Method {
+			return nil, ErrInvalidSigningAlgorithm
+		}
+		if mw.usingPublicKeyAlgo() {
+			return mw.pubKey, nil
+		}
+
+		// save token string if vaild
+		c.Set("JWT_TOKEN", token)
+
+		return mw.Key, nil
+	})
+}
+
+// ParseTokenString parse jwt token string
+func (mw *GinJWTMiddleware) ParseTokenString(token string) (*jwt.Token, error) {
+	return jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
+		if jwt.GetSigningMethod(mw.SigningAlgorithm) != t.Method {
+			return nil, ErrInvalidSigningAlgorithm
+		}
+		if mw.usingPublicKeyAlgo() {
+			return mw.pubKey, nil
+		}
+
+		return mw.Key, nil
+	})
+}
+
+func (mw *GinJWTMiddleware) unauthorized(c *gin.Context, code int, message string) {
+	c.Header("WWW-Authenticate", "JWT realm="+mw.Realm)
+	if !mw.DisabledAbort {
+		c.Abort()
+	}
+
+	mw.Unauthorized(c, code, message)
+}
+
+// ExtractClaims help to extract the JWT claims
+func ExtractClaims(c *gin.Context) MapClaims {
+	claims, exists := c.Get("JWT_PAYLOAD")
+	if !exists {
+		return make(MapClaims)
+	}
+
+	return claims.(MapClaims)
+}
+
+// ExtractClaimsFromToken help to extract the JWT claims from token
+func ExtractClaimsFromToken(token *jwt.Token) MapClaims {
+	if token == nil {
+		return make(MapClaims)
+	}
+
+	claims := MapClaims{}
+	for key, value := range token.Claims.(jwt.MapClaims) {
+		claims[key] = value
+	}
+
+	return claims
+}
+
+// GetToken help to get the JWT token string
+func GetToken(c *gin.Context) string {
+	token, exists := c.Get("JWT_TOKEN")
+	if !exists {
+		return ""
+	}
+
+	return token.(string)
+}

+ 25 - 0
tools/md5.go

@@ -0,0 +1,25 @@
+package tools
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"io"
+	"strings"
+)
+
+// Aes加密
+func MD5(sourceValue string) string {
+	w := md5.New()
+	io.WriteString(w, sourceValue)
+	newMD5 := fmt.Sprintf("%x", w.Sum(nil))
+
+	return strings.ToLower(newMD5)
+}
+
+//返回一个32位md5加密后的字符串
+func GetMD5Encode(data string) string {
+	h := md5.New()
+	h.Write([]byte(data))
+	return hex.EncodeToString(h.Sum(nil))
+}

+ 55 - 0
tools/snowflake.go

@@ -0,0 +1,55 @@
+package tools
+
+import (
+	"errors"
+	"sync"
+	"time"
+)
+
+const (
+	workerBits  uint8 = 10
+	numberBits  uint8 = 12
+	workerMax   int64 = -1 ^ (-1 << workerBits)
+	numberMax   int64 = -1 ^ (-1 << numberBits)
+	timeShift   uint8 = workerBits + numberBits
+	workerShift uint8 = numberBits
+	startTime   int64 = 1525705533000 // 如果在程序跑了一段时间修改了epoch这个值 可能会导致生成相同的ID
+)
+
+type Worker struct {
+	mu        sync.Mutex
+	timestamp int64
+	workerId  int64
+	number    int64
+}
+
+func NewWorker(workerId int64) (*Worker, error) {
+	if workerId < 0 || workerId > workerMax {
+		return nil, errors.New("Worker ID excess of quantity")
+	}
+	// 生成一个新节点
+	return &Worker{
+		timestamp: 0,
+		workerId:  workerId,
+		number:    0,
+	}, nil
+}
+
+func (w *Worker) GetId() int64 {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	now := time.Now().UnixNano() / 1e6
+	if w.timestamp == now {
+		w.number++
+		if w.number > numberMax {
+			for now <= w.timestamp {
+				now = time.Now().UnixNano() / 1e6
+			}
+		}
+	} else {
+		w.number = 0
+		w.timestamp = now
+	}
+	ID := int64((now-startTime)<<timeShift | (w.workerId << workerShift) | (w.number))
+	return ID
+}

+ 144 - 0
tools/string.go

@@ -0,0 +1,144 @@
+package tools
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"io/ioutil"
+	"strconv"
+	"time"
+)
+
+func StringToInt64(e string) (int64, error) {
+	return strconv.ParseInt(e, 10, 64)
+}
+
+func StringToInt(e string) (int, error) {
+	return strconv.Atoi(e)
+}
+
+func GetCurrntTimeStr() string {
+
+	NowTimeZone := time.FixedZone("CST", 8*3600)
+	//time.Local, _ = time.LoadLocation("Asia/Chongqing")
+	//timelocal := time.LoadLocation("Asia/Chongqing")
+	//time.Local = timelocal
+	//time.LoadLocation("Asia/Chongqing")
+	//return time.Now().Local().Format("2006-01-02 15:04:05")
+	return time.Now().In(NowTimeZone).Format("2006-01-02 15:04:05")
+}
+
+func SubTimeSecond(inDate string) int64 {
+	NowTimeZone := time.FixedZone("CST", 8*3600)
+	timeLayout := "2006-01-02 15:04:05" //转化所需模板
+
+	stamp, _ := time.ParseInLocation(timeLayout, inDate, time.Local)
+	sr := stamp.Unix()
+	timestamp := time.Now().In(NowTimeZone).Format("2006-01-02 15:04:05")
+	now, _ := time.ParseInLocation(timeLayout, timestamp, time.Local)
+
+	return now.Unix() - sr
+}
+
+//获取日期
+func GetCurrntDateStr() string {
+	NowTimeZone := time.FixedZone("CST", 8*3600)
+	return time.Now().In(NowTimeZone).Format("2006-01-02")
+}
+
+func GetPayRemainingTime(inDate string) int64 {
+	NowTimeZone := time.FixedZone("CST", 8*3600)
+	timeLayout := "2006-01-02 15:04:05" //转化所需模板
+	//fmt.Println("inDate = ", inDate)
+	stamp, _ := time.ParseInLocation(timeLayout, inDate, time.Local)
+	sr := stamp.Unix()
+	timestamp := time.Now().In(NowTimeZone).Format("2006-01-02 15:04:05")
+	now, _ := time.ParseInLocation(timeLayout, timestamp, time.Local)
+
+	fmt.Println("sr = ", sr)
+	fmt.Println("timestamp", now.Unix())
+	return 900 - (now.Unix() - sr)
+}
+
+//输入日期减去当前日期
+func InDateSubNow(inDate string) int64 {
+	NowTimeZone := time.FixedZone("CST", 8*3600)
+	timeLayout := "2006-01-02 15:04:05" //转化所需模板
+	//fmt.Println("inDate = ", inDate)
+	stamp, _ := time.ParseInLocation(timeLayout, inDate, time.Local)
+	sr := stamp.Unix()
+	timestamp := time.Now().In(NowTimeZone).Format("2006-01-02 15:04:05")
+	now, _ := time.ParseInLocation(timeLayout, timestamp, time.Local)
+
+	return sr - now.Unix()
+}
+
+//输入日期相减得出天数
+func DateSubDate(startDate string, endDate string) (float64, error) {
+	timeLayout := "2006-01-02" //转化所需模板
+	start, err := time.ParseInLocation(timeLayout, startDate, time.Local)
+	if err != nil {
+		return 0, err
+	}
+	end, err := time.ParseInLocation(timeLayout, endDate, time.Local)
+	if err != nil {
+		return 0, err
+	}
+	return start.Sub(end).Hours(), nil
+
+}
+
+//加天数
+func DateAdd(startDate string, num time.Duration) (string, error) {
+	timeLayout := "2006-01-02" //转化所需模板
+	start, err := time.ParseInLocation(timeLayout, startDate, time.Local)
+	if err != nil {
+		return "", err
+	}
+	return start.Add(num).Format("2006-01-02"), nil
+}
+
+func GetCurrntTime() time.Time {
+	NowTimeZone := time.FixedZone("CST", 8*3600)
+	return time.Now().In(NowTimeZone)
+}
+
+func StructToJsonStr(e interface{}) (string, error) {
+	if b, err := json.Marshal(e); err == nil {
+		return string(b), err
+	} else {
+		return "", err
+	}
+}
+
+func GetBodyString(c *gin.Context) (string, error) {
+	body, err := ioutil.ReadAll(c.Request.Body)
+	if err != nil {
+		fmt.Printf("read body err, %v\n", err)
+		return string(body), nil
+	} else {
+		return "", err
+	}
+}
+
+func JsonStrToMap(e string) (map[string]interface{}, error) {
+	var dict map[string]interface{}
+	if err := json.Unmarshal([]byte(e), &dict); err == nil {
+		return dict, err
+	} else {
+		return nil, err
+	}
+}
+
+func StructToMap(data interface{}) (map[string]interface{}, error) {
+	dataBytes, err := json.Marshal(data)
+	if err != nil {
+		return nil, err
+	}
+	mapData := make(map[string]interface{})
+	err = json.Unmarshal(dataBytes, &mapData)
+	if err != nil {
+		return nil, err
+	}
+	return mapData, nil
+}

+ 1 - 0
tools/utils.go

@@ -0,0 +1 @@
+package tools