Explorar el Código

wip:回调发邮件提交

wangkaiyue hace 1 año
padre
commit
eeaf5cdd7e

+ 21 - 1
config.yaml

@@ -8,7 +8,9 @@ reqDebug: true # 是否打印接口调用日志
 
 logger:
   level: "all"
-  stdout: true
+  #  stdout: true
+  path: "logs" # 日志文件路径。默认为空,表示关闭,仅输出到终端
+  file: "{Y-m-d}.log" # 日志文件格式。默认为"{Y-m-d}.log"
 
 database:
   default:
@@ -36,6 +38,12 @@ company:
   tel: "13733157437"
   password: "13733157437Ph"
 
+# 发票三方系统配置-接口回调
+callBack:
+  #flush: true #是否同步回调地址 默认false
+  action: "https://wky.jydev.jianyu360.com/Invoice/callback"
+
+
 # 开票任务
 invoiceJob:
   stop: false # 是否停止任务(临时停止任务)
@@ -47,5 +55,17 @@ qwxRobotUrl: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=e7792e7a-159d
 
 pdfFilePathRootDir: "./out"
 pdfFilePathPrefix: "jyInvoice" #即请求路径前缀
+pdfFilePathDomain: "https://jianyu360.cn"
+
+
+mailConfig:
+  - addr: "smtp.exmail.qq.com"
+    port: 465
+    pwd: "ue9Rg9Sf4CVtdm5a"
+    user: "public03@topnet.net.cn"
+  - addr: "smtp.exmail.qq.com"
+    port: 465
+    pwd: "ue9Rg9Sf4CVtdm5a"
+    user: "public03@topnet.net.cn"
 
 

+ 13 - 0
go.mod

@@ -3,6 +3,7 @@ module ElectronicInvoice
 go 1.18
 
 require (
+	app.yhyue.com/moapp/jybase v0.0.0-20240422010359-27408422af30
 	github.com/gogf/gf/contrib/drivers/mysql/v2 v2.7.0
 	github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.0
 	github.com/gogf/gf/v2 v2.7.0
@@ -20,20 +21,32 @@ require (
 	github.com/go-logr/logr v1.2.4 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-sql-driver/mysql v1.7.1 // indirect
+	github.com/go-stack/stack v1.8.0 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
 	github.com/gorilla/websocket v1.5.0 // indirect
 	github.com/grokify/html-strip-tags-go v0.0.1 // indirect
+	github.com/klauspost/compress v1.13.6 // indirect
 	github.com/magiconair/properties v1.8.6 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/redis/go-redis/v9 v9.2.1 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.0.2 // indirect
+	github.com/xdg-go/stringprep v1.0.2 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+	go.mongodb.org/mongo-driver v1.9.1 // indirect
 	go.opentelemetry.io/otel v1.14.0 // indirect
 	go.opentelemetry.io/otel/sdk v1.14.0 // indirect
 	go.opentelemetry.io/otel/trace v1.14.0 // indirect
+	golang.org/x/crypto v0.14.0 // indirect
 	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
 	golang.org/x/sys v0.13.0 // indirect
 	golang.org/x/text v0.13.0 // indirect
+	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )

+ 356 - 1
go.sum

@@ -1,38 +1,163 @@
+app.yhyue.com/moapp/esv1 v0.0.0-20220414031211-3da4123e648d/go.mod h1:91/lSD/hS+ckMVP3WdidRzDhC60lLMdyce9QHy0cSMA=
+app.yhyue.com/moapp/jybase v0.0.0-20240422010359-27408422af30 h1:d3EW7fnnsIUtXCeYvPRvRr7NfrKRZwQRWOV8DJpN+T4=
+app.yhyue.com/moapp/jybase v0.0.0-20240422010359-27408422af30/go.mod h1:XHNATN6tsJKHdCB0DbUtFdPPHXexTUFyB3RlO+lUUoM=
+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/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
 github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/RoaringBitmap/roaring v1.5.0/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/aws/aws-sdk-go v1.35.20/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
 github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
 github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
 github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coscms/tagfast v0.0.0-20150925144250-2b69b2496250/go.mod h1:zX8vynptAghuV/KG8BOZlDeo4DsTKWfBQ154RWlkay0=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 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/donnie4w/go-logger v0.0.0-20170827050443-4740c51383f4/go.mod h1:L7S4x0R7vv3xoOhGuyAJyCO2MYzWOpccM4Isn8jIUgY=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
 github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+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=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
 github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogf/gf/contrib/drivers/mysql/v2 v2.7.0 h1:5Igvtz4gy5UMvH+Ut4kLIpwSzggV9ZgDVBsIiOctH5E=
 github.com/gogf/gf/contrib/drivers/mysql/v2 v2.7.0/go.mod h1:0+flZ0clMKjtH1sTI7YD2KG4FPr8xz0L9h1WMd5M2Z8=
 github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.0 h1:hJxshC0gZyFZaGo2HItXd5XMzIMbCRcgShr1ljMYwUc=
 github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.0/go.mod h1:is3Q3ItZSPMZ1RBJ3xIcEasyGZnOg8eNeG9dubOx/zc=
 github.com/gogf/gf/v2 v2.7.0 h1:CjxhbMiE7oqf6K8ZtGuKt3dQEwK4vL6LhiI+dI7tJGU=
 github.com/gogf/gf/v2 v2.7.0/go.mod h1:Qu8nimKt9aupJQcdUL85tWF4Mfxocz97zUt8UC4abVI=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
+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.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
 github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
 github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -41,34 +166,264 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+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/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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
+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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/olivere/elastic v6.2.37+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8=
+github.com/olivere/elastic/v7 v7.0.22/go.mod h1:VDexNy9NjmtAkrjNoI7tImv7FR4tf5zUA3ickqu5Pc8=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg=
 github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
+github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
+github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
+github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
+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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
+go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c=
+go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
 go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
 go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
 go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
 go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
 go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+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-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-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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+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-20200107190931-bf48bf16ab8d/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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20181116152217-5ac8a444bdc5/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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
 golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+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=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
+gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
 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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
+gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+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=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
+gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

+ 3 - 4
internal/cmd/cmd.go

@@ -19,10 +19,9 @@ var (
 			s := g.Server()
 			s.Group("/Invoice", func(group *ghttp.RouterGroup) {
 				group.POST("/callback", controller.CallBack) //开票回调
-				group.POST("/control", controller.CallBack)  //控制开票接口 控制开关
-
-				//group.GET("/Add", controller.InvoiceAdd)         //开票
-				//group.GET("/Replace", controller.InvoiceReplace) //换票(红冲+开票)
+				group.ALL("/control", controller.Control)    //控制开票接口 控制开关
+				//group.GET("/Add", controller.InvoiceAdd)          //开票
+				//group.GET("/Replace", controller.InvoiceReplace)  //换票(红冲+开票)
 			})
 			s.Run()
 			return nil

+ 6 - 0
internal/consts/consts.go

@@ -2,9 +2,15 @@ package consts
 
 import (
 	"context"
+	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/frame/g"
 )
 
 var (
 	ServiceAddress = g.Cfg().MustGet(context.Background(), "tripartite.service").String()
 )
+
+var (
+	LoginOutErr = gerror.New("请重新获取授权")
+	AuthTimeOut = gerror.New("验证超时")
+)

+ 24 - 3
internal/controller/callback.go

@@ -4,6 +4,7 @@ import (
 	"ElectronicInvoice/internal/service"
 	"github.com/gogf/gf/v2/frame/g"
 	"github.com/gogf/gf/v2/net/ghttp"
+	"net/url"
 	"strings"
 )
 
@@ -12,18 +13,19 @@ func CallBack(r *ghttp.Request) {
 	err := func() error {
 		callType := r.Get("calltype").String()
 		if g.Cfg().MustGet(r.Context(), "reqDebug", false).Bool() {
-			g.Log().Infof(r.Context(), "%s\ncallType: %s\ncallBack: %s", strings.Repeat("=", 50), callType, string(r.GetBody()))
+			g.Log().Infof(r.Context(), "%s\n接口回调 信息调试\ncallType: %s\ncallBack form: %+v", strings.Repeat("=", 50), callType, printFilter(r.Request.Form))
 		}
 
 		switch callType {
 		case "userQuit": //用户退出登录
+			service.JyInvoiceManager.Login = false
 			g.Log().Info(r.Context(), "长在线退出登录")
 		case "Invoicing": //开发票回调
 			return service.InvoicingCallBackLogic(r)
 		case "InvoicingAll": //批量开票
 			g.Log().Info(r.Context(), "用户退出登录")
-		case "Offset":
-			g.Log().Info(r.Context(), "开红票:%s")
+		case "offset":
+			return service.InvoicingMakeRedCallBackLogic(r)
 		case "livenessDetection":
 			g.Log().Info(r.Context(), "活体认证:%s")
 		default:
@@ -37,3 +39,22 @@ func CallBack(r *ghttp.Request) {
 	}
 	r.Response.WriteJson(g.MapStrStr{"code": "200"})
 }
+
+// printFilter 打印参数调试
+func printFilter(value url.Values) map[string][]string {
+	var filter = []string{"pdf"} //过滤部分字段
+	res := map[string][]string{}
+	for key, val := range value {
+		add := true
+		for _, v := range filter {
+			if key == v {
+				add = false
+				break
+			}
+		}
+		if add {
+			res[key] = val
+		}
+	}
+	return res
+}

+ 37 - 0
internal/controller/control.go

@@ -0,0 +1,37 @@
+package controller
+
+import (
+	"ElectronicInvoice/internal/service"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/net/ghttp"
+)
+
+func Control(r *ghttp.Request) {
+	err := func() error {
+		switch r.Get("t").String() {
+		case "mvc": //Mobile verification code 验证码操作
+			code := r.Get("code").String()
+			if code == "clear" {
+				return service.JyInvoiceManager.MobileVerificationClear()
+			} else {
+				return service.JyInvoiceManager.MobileVerificationCode(code)
+			}
+		case "run": //控制程序暂停
+			g.Log().Infof(r.Context(), "runing")
+		default:
+			return gerror.New("未知操作")
+		}
+		return nil
+	}()
+	if err != nil {
+		r.Response.WriteJson(g.Map{
+			"flag": "fail",
+			"err":  err.Error(),
+		})
+	} else {
+		r.Response.WriteJson(g.Map{
+			"flag": "success",
+		})
+	}
+}

+ 93 - 33
internal/service/invoiceCallback.go

@@ -2,66 +2,126 @@ package service
 
 import (
 	"ElectronicInvoice/util"
-	"encoding/base64"
+	"context"
 	"fmt"
 	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/frame/g"
 	"github.com/gogf/gf/v2/net/ghttp"
-	"github.com/gogf/gf/v2/os/gfile"
-	"net/url"
+	"github.com/gogf/gf/v2/util/gconv"
+	"strings"
 	"time"
 )
 
 // InvoicingCallBackLogic 开票回调逻辑
 func InvoicingCallBackLogic(r *ghttp.Request) error {
-	tType := r.Get("type").Int() //type [0 需要活体检测;1 成功 返回base64]
+	JyInvoiceManager.ReleasePool() //可进行下次开票请求
+	tType := r.Get("type").Int()   //type [0 需要活体检测;1 成功 返回base64]
 	switch tType {
 	case 0: //需要活体检测
-		imgVal, err := url.QueryUnescape(r.Get("img").String())
-		if err != nil {
-			return gerror.Wrap(err, "活体检测二维码解析失败")
-		}
-		if err := util.SendQrImage2ChatBot(imgVal); err != nil {
+		//imgVal, err := url.QueryUnescape(r.Get("img").String())
+		//if err != nil {
+		//	return gerror.Wrap(err, "活体检测二维码解析失败")
+		//}
+		JyInvoiceManager.OCRPass = false
+		if err := util.SendQrImage2ChatBot(r.Get("img").String()); err != nil {
 			return gerror.Wrap(err, "发送活体检测消息出错")
 		}
+		g.Log().Info(r.Context(), "需要活体认证,已发送二维码消息")
 	case 1: //开票成功
-		pdfBase64 := r.Get("pdf").String()
-		orderCode := r.Get("id").String()
-		data, err := base64.StdEncoding.DecodeString(pdfBase64)
-		if err != nil {
-			return gerror.Wrap(err, "解析文件base64编码失败")
-		}
 		var (
-			rootDir      = g.Cfg().MustGet(r.Context(), "pdfFilePathRootDir", "/out").String()
-			pathPrefix   = g.Cfg().MustGet(r.Context(), "pdfFilePathPrefix", "/invoice").String()
-			now          = time.Now()
-			requestPath  = fmt.Sprintf("/%s/%s/file_%d.pdf", pathPrefix, now.Format("2006-01-02"), now.Unix())
-			fileFullPath = fmt.Sprintf("%s%s", rootDir, requestPath)
+			pdfBase64 = r.Get("pdf").String()
+			orderCode = r.Get("id").String()
+			num       = r.Get("num").String()
+			kptime    = r.Get("kptime").String()
+
+			invoiceTime time.Time
+			err         error
+			path        string
 		)
-		pdfFile, err := gfile.Create(fileFullPath)
+		if pdfBase64 == "" || orderCode == "" || num == "" || kptime == "" {
+			return gerror.New("缺少回调参数")
+		}
+
+		invoiceTime, err = time.ParseInLocation(kptime, time.DateTime, time.Local)
 		if err != nil {
-			return gerror.Wrap(err, "创建pdf文件异常")
+			return gerror.Wrap(err, "时间格式化异常")
 		}
-		if _, err = pdfFile.Write(data); err != nil {
-			return gerror.Wrap(err, "pdf文件写入异常")
+		path, err = util.SavePdfFile(r.Context(), num, pdfBase64)
+		if err != nil {
+			return gerror.Wrap(err, "保存pdf文件失败")
 		}
-		g.Log().Infof(r.Context(), "pdf保存成功 orderCode:%s filePath:%s ", orderCode, fileFullPath)
-		return nil
+		g.Log().Infof(r.Context(), "pdf保存成功 orderCode:%s filePath:%s ", orderCode, path)
+		return updateOrderInvoiceStatus(r.Context(), orderCode, num, invoiceTime, path)
 	case 3:
-		g.Log().Error(r.Context(), "活体验证已过期,未完成活体验证")
+		g.Log().Info(r.Context(), "活体验证已过期,未完成活体验证")
 	case 4:
-		g.Log().Error(r.Context(), "系统错误")
+		g.Log().Info(r.Context(), "系统错误")
 	case 6:
-		g.Log().Error(r.Context(), "税率不存在")
+		g.Log().Info(r.Context(), "税率不存在")
 	case 7:
-		g.Log().Error(r.Context(), "非数电票试点纳税人,未核定数电票票种,不允许开票或其他原因")
+		g.Log().Info(r.Context(), "非数电票试点纳税人,未核定数电票票种,不允许开票或其他原因")
 	default:
-		g.Log().Infof(r.Context(), "InvoicingCallBackLogic tType:%s", tType)
+		g.Log().Info(r.Context(), "InvoicingCallBackLogic tType:%s", tType)
 	}
 	return nil
 }
 
-// makeInvoiceSuccess  开票成功
-func makeInvoiceSuccess() error {
+// updateOrderInvoiceStatus  开票成功进行数据库操作
+// orderCode 订单编号
+// invoiceNum 发票号码
+// createData 开票时间
+// pdfPath 下载地址
+func updateOrderInvoiceStatus(ctx context.Context, orderCode, invoiceNum string, invoiceTime time.Time, pdfPath string) error {
+	var (
+		queryItem = "order_code"
+		err       error
+	)
+	if strings.Contains(orderCode, "xx") {
+		//自助开票
+		queryItem = "only_Identifying"
+	}
+	_, err = g.DB().Update(ctx, "invoice", g.Map{
+		"invoice_number": invoiceNum,
+		"url":            pdfPath,
+		"billing_time":   invoiceTime.Unix(),
+		"invoice_status": 1,
+	}, fmt.Sprintf("invoice_changed = 0 AND %s ", queryItem), orderCode)
+	if err != nil {
+		return gerror.Wrap(err, "更新发票状态异常")
+	}
+
+	// 王浩说开票成功 订单状态不用更新;以下代码来自王浩
+	//if strings.Contains(orderCode, "xx") {
+	//	selfInvoicing(ctx, orderCode)
+	//}
+
+	if err := SendInvoiceSuccessMail(ctx, orderCode); err != nil {
+		g.Log().Errorf(ctx, "%s 发送邮件失败 err: %v ", orderCode, err)
+	}
 	return nil
 }
+
+// selfInvoicing 自助开票
+func selfInvoicing(ctx context.Context, orderCode string) {
+	//自助开票
+	res, err := g.DB().GetOne(ctx, "SELECT GROUP_CONCAT(order_code) as  order_code  from invoice where only_Identifying=?", orderCode)
+	if err != nil {
+		g.Log().Errorf(ctx, "自助开票异常")
+
+	}
+	orderArr := strings.Split(gconv.String(res["order_code"]), ",")
+	for _, v := range orderArr {
+		if v == "" {
+			continue
+		}
+		updateData := map[string]interface{}{}
+		//if InvoiceStatusHandle(v) {
+		//	updateData["applybill_status"] = 2
+		//} else {
+		//	updateData["applybill_status"] = 3
+		//}
+		g.DB().Update(ctx, "dataexport_order", map[string]interface{}{
+			"order_code": v,
+		}, updateData)
+	}
+}

+ 32 - 0
internal/service/invoiceCallback_red.go

@@ -0,0 +1,32 @@
+package service
+
+import (
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/net/ghttp"
+)
+
+// InvoicingMakeRedCallBackLogic 开票回调逻辑
+func InvoicingMakeRedCallBackLogic(r *ghttp.Request) error {
+	tType := r.Get("type").Int()
+	switch tType {
+	case 0: //红冲成功
+	case 1: //失败
+	case 2: //授信额度等信息
+		g.Log().Info(r.Context(), "授信额度等信息")
+	case 3:
+		g.Log().Info(r.Context(), "活体验证已过期,未完成活体验证")
+	case 4:
+		g.Log().Info(r.Context(), "活体成功")
+	case 5:
+		g.Log().Info(r.Context(), "返回活体检测二维码的内容")
+	case 9:
+		g.Log().Info(r.Context(), "活体码未知错误")
+	case 10:
+		g.Log().Info(r.Context(), "非数电票试点纳税人,未核定数电票票种,不允许开票")
+	case 11:
+		g.Log().Info(r.Context(), "获取授信额度失败")
+	default:
+		g.Log().Info(r.Context(), "InvoicingMakeRedCallBackLogic tType:%s", tType)
+	}
+	return nil
+}

+ 97 - 0
internal/service/invoiceDemo.go

@@ -0,0 +1,97 @@
+package service
+
+import (
+	"ElectronicInvoice/internal/consts"
+	"context"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/frame/g"
+	"time"
+)
+
+func (im *InvoiceManager) Demo(ctx context.Context) {
+	c := MakeInvoiceData{
+		Type:      "2",
+		Gmfmc:     "测试个人",
+		Gmfnsrsbh: "",
+		Id:        "0888886",
+		InvoiceArr: []MakeInvoiceItems{{
+			Xmmc:     "0fccdac71c36a8552ba662e7a2f42726",
+			WhStatus: 1,
+			Je:       "2",
+			Sl:       "1",
+		}},
+	}
+	err := im.Auth.MakeSingleInvoice(c)
+	if err != nil {
+		g.Log().Errorf(ctx, "开票失败 %v", err)
+	}
+}
+
+func (im *InvoiceManager) RedDemo(ctx context.Context) {
+	err := im.Auth.MakeSingleRedInvoice(MakeRedInvoiceData{
+		Num:  "24112000000026895182",
+		Date: "2024-04-22",
+	})
+	if err != nil {
+		g.Log().Errorf(ctx, "红冲失败 %v", err)
+	}
+}
+
+func (im *InvoiceManager) RunOneJob(ctx context.Context) {
+	t := 0
+start:
+	if im.Login != true {
+		for i := 0; i < 10; i++ {
+			if err := im.Auth.Login(); err != nil {
+				g.Log().Errorf(ctx, "模拟登录异常 %v", err)
+			} else {
+				g.Log().Infof(ctx, "登录成功")
+				break
+			}
+			time.Sleep(time.Minute)
+		}
+	}
+
+	if !im.Login {
+		g.Log().Infof(ctx, "登录失败退出")
+		return
+	}
+	//for i := 0; i < 3; i++ {
+
+	select {
+	case im.runPool <- true:
+	case <-time.After(time.Minute):
+		g.Log().Errorf(ctx, "RunJob-开票等待异常,结束此次任务")
+		return
+	}
+
+	c := MakeInvoiceData{
+		Type:      "2",
+		Gmfmc:     "测试个人",
+		Gmfnsrsbh: "",
+		Id:        "0888886",
+		InvoiceArr: []MakeInvoiceItems{{
+			Xmmc:     "0fccdac71c36a8552ba662e7a2f42726",
+			WhStatus: 1,
+			Je:       "2",
+			Sl:       "1",
+		}},
+	}
+	err := im.Auth.MakeSingleInvoice(c)
+	if err != nil {
+		im.ReleasePool()
+	}
+	if gerror.Is(err, consts.LoginOutErr) {
+		g.Log().Infof(ctx, "RunJob-身份过期,需要重新登录")
+		if t == 0 {
+			t = t + 1
+			time.Sleep(time.Second * 5)
+			goto start
+		}
+	}
+
+	if err != nil {
+		g.Log().Errorf(ctx, "RunJob-开票接口调用异常 %v", err)
+	}
+	//}
+}

+ 67 - 19
internal/service/invoiceManager.go

@@ -2,32 +2,42 @@ package service
 
 import (
 	"context"
+	"github.com/gogf/gf/v2/database/gdb"
+	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/frame/g"
-	"github.com/gogf/gf/v2/os/gcron"
 	"time"
 )
 
 var (
-	jyInvoiceManager *InvoiceManager
+	JyInvoiceManager *InvoiceManager
 )
 
 type InvoiceManager struct {
-	Auth    *TripartiteAuth
-	runPool chan bool
+	Auth      *TripartiteAuth
+	Login     bool        //登录状态
+	OCRPass   bool        //活体检测状态是否通过
+	runPool   chan bool   //任务(每次只能进行一个开票任务)
+	phoneCode chan string //短信验证码池
 }
 
 func init() {
-	jyInvoiceManager = createInvoiceManager()
-	_, err := gcron.Add(context.Background(), "", jyInvoiceManager.RunJob, "invoiceJob")
-	if err != nil {
-		panic(err)
-	}
+	JyInvoiceManager = createInvoiceManager()
+	//_, err := gcron.Add(context.Background(), "", JyInvoiceManager.RunJob, "invoiceJob")
+	//if err != nil {
+	//	panic(err)
+	//}
+	//go JyInvoiceManager.RunOneJob(context.Background())
+	//go JyInvoiceManager.Demo(context.Background()) //开票
+	//go JyInvoiceManager.RedDemo(context.Background())//红冲
 }
 
 func createInvoiceManager() *InvoiceManager {
 	return &InvoiceManager{
-		Auth:    createTripartite(),
-		runPool: make(chan bool, 1),
+		Auth:      createTripartite(),
+		Login:     true, //默认已经登录
+		OCRPass:   true,
+		runPool:   make(chan bool, 1),   //开票只能单线程跑
+		phoneCode: make(chan string, 1), //手机验证码
 	}
 }
 
@@ -35,24 +45,63 @@ func (im *InvoiceManager) ReleasePool() {
 	<-im.runPool
 }
 
+func (im *InvoiceManager) MobileVerificationCode(code string) error {
+	if code == "" {
+		return gerror.New("验证码为空")
+	}
+	select {
+	case <-time.After(time.Minute):
+		return gerror.New("验证码接受超时")
+	case im.phoneCode <- code:
+		return nil
+	}
+}
+
+func (im *InvoiceManager) MobileVerificationClear() error {
+	select {
+	case <-time.After(time.Minute):
+		return gerror.New("清除验证码超时")
+	case <-im.phoneCode:
+		return nil
+	}
+}
+
 // RunJob 开票定时任务
 func (im *InvoiceManager) RunJob(ctx context.Context) {
 	if g.Cfg().MustGet(ctx, "invoiceJob.stop", false).Bool() {
 		g.Log().Infof(ctx, "RunJob-开票程序任务已暂停,开启请删除 config.json > invoiceJob.stop")
 		return
 	}
+
+	total, okNum, err := im.simpleMakeInvoice(ctx)
+	if err != nil {
+		g.Log().Errorf(ctx, "开蓝票任务异常 %v", err)
+	} else {
+		g.Log().Infof(ctx, "开蓝票任务完成 共%d个 完成%d个", total, okNum)
+	}
+
+	g.Log().Infof(ctx, "RunJob-开票任务完成")
+}
+
+// simpleMakeInvoice 简单开票
+func (im *InvoiceManager) simpleMakeInvoice(ctx context.Context) (total, okNum int64, err error) {
+	var (
+		res gdb.Result
+	)
+	total, okNum = -1, -1
 	//查询需要开票的数据
-	res, err := g.DB().Query(ctx, "SELECT * FROM invoice WHERE invoice_status=0 AND invoice_variety='电子普通发票'")
+	res, err = g.DB().Query(ctx, "SELECT * FROM invoice WHERE invoice_status=0 AND invoice_order_code is NULL")
 	if err != nil {
-		g.Log().Errorf(ctx, "RunJob-查询待开票异常 %s", err)
+		g.Log().Errorf(ctx, "RunJob-simpleMakeInvoice-查询待开票异常 %s", err)
+		return -1, -1, gerror.Wrap(err, "simpleMakeInvoice-查询待开票异常")
 	}
-	g.Log().Infof(ctx, "RunJob-本次共加载%d条开票记录", res.Len())
+	g.Log().Infof(ctx, "RunJob-simpleMakeInvoice-本次共加载%d条开票记录", res.Len())
 
 	for i, m := range res.List() {
 		select {
 		case im.runPool <- true:
-		case <-time.After(time.Minute):
-			g.Log().Errorf(ctx, "RunJob-开票等待异常,结束此次任务 %s", err)
+		case <-time.After(time.Minute * 5):
+			g.Log().Errorf(ctx, "RunJob-simpleMakeInvoice-开票等待异常,结束此次任务")
 			return
 		}
 		g.Dump(i, m)
@@ -77,10 +126,9 @@ func (im *InvoiceManager) RunJob(ctx context.Context) {
 		err := im.Auth.MakeSingleInvoice(c)
 		if err != nil {
 			im.ReleasePool()
-			g.Log().Errorf(ctx, "RunJob-开票接口调用异常 %v", err)
+			g.Log().Errorf(ctx, "RunJob-simpleMakeInvoice-开票接口调用异常 %v", err)
 			continue
 		}
 	}
-
-	g.Log().Infof(ctx, "RunJob-开票任务完成")
+	return
 }

+ 202 - 0
internal/service/sendMail.go

@@ -0,0 +1,202 @@
+package service
+
+import (
+	"app.yhyue.com/moapp/jybase/mail"
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/frame/g"
+	"strings"
+	"time"
+)
+
+type (
+	mailConfig struct {
+		Addr string
+		Port int
+		Pwd  string
+		User string
+	}
+
+	invoiceRecord struct {
+		InvoiceType      string `json:"invoice_type"`
+		Mail             string `json:"mail"`
+		TaxpayerIdentnum string `json:"taxpayer_identnum"`
+		CompanyName      string `json:"company_name"`
+		Phone            string `json:"phone"`
+		Url              string `json:"url"`
+		OrderCode        string `json:"order_code"`
+	}
+)
+
+var (
+	GmailAuth     []*mail.GmailAuth
+	mailConfigArr []*mailConfig
+)
+
+const (
+	emailHtml_header = `<!DOCTYPE html>
+					<html lang="en">
+					<head>
+						<meta charset="UTF-8">
+						<title>电子发票</title>
+					</head>
+					<style type="text/css">
+						html, body,a,div,p {
+							margin:0;
+							padding:0;
+						}
+						.jymail a{text-decoration: none !important;font-family: "PingFang SC";font-size: 15px;}
+						.email{width: 680px;border: 1px solid #E0E0E0;background: #fff;margin: 20px auto;border-radius: 8px;}
+						.jymail .top{height: 80px;width:100%%;padding: 0 40px;margin:0 auto;background-color: #2CB7CA;border-radius:8px 8px 0 0;clear: both;box-sizing: border-box;}
+						.jymail .left{float:left;width:120px;height: 32px;margin: 20px 0;}
+						.jymail .left img{width:100%%;height: 100%%;}
+						.jymail .right{float: right;margin: 30px 0;}
+						.jymail .right a{color: #fff;font-size: 14px;line-height: 18px;}
+						.jymail .middle{width: 100%%;padding: 64px 40px;box-sizing: border-box;}
+						.jymail .user{font-size: 18px;line-height: 24px;color: #1D1D1D;padding-bottom: 32px;}
+						.jymail .text{font-size: 13px;line-height: 20px;color: #1D1D1D;}
+						.jymail .download{display: inline-block;width: 90px;height: 30px;background-color: #2CB7CA;color: #fff;text-align: center;border-radius: 6px;line-height: 30px;}
+						.jymail .warning{font-size: 12px;line-height: 20px;color: red;color: #FE737A;}
+						.jymail .bottom{margin-top:32px;border-top: 4px solid #2CB7CA;background: #F5F5FB;padding: 32px 40px;box-sizing: border-box;border-bottom-left-radius: 8px;border-bottom-right-radius: 8px;}
+						.jymail .item{margin-bottom: 10px;font-size: 12px;line-height: 24px;}
+						.jymail .label{display: inline-block; min-width: 92px;color: #686868;}
+						.jymail .value{color: #1D1D1D;}
+					</style>
+					<body>
+					<div class="email jymail">
+						<div class="top">
+							<div class="left">
+								<img src="https://www.jianyu360.com/images/pc/logo.png">
+							</div>
+							<div class="right">
+								<a href="https://www.jianyu360.com">剑鱼首页</a>
+								<span style="color: #fff;margin: 0 12px;">|</span>
+								<a href="https://www.jianyu360.com/front/swordfish/toMyOrder">我的订单</a>
+							</div>
+						</div>
+						<div class="middle">
+							<p class="user">尊敬的用户您好!</p><b></b>
+							<p class="text">感谢您在剑鱼标讯(<a href="https://www.jianyu360.com" style="color: #2CB7CA;">jianyu360.com</a>)购物!</p>
+							<p class="text">剑鱼标讯已为您的订单: <span id="order_code">%s</span> 开具%s,总共 1 张。</p>
+							<div style="padding: 32px 0;">
+								  %s
+							</div>
+							<p class="warning">安全提醒:剑鱼标讯平台及销售商不会以订单异常、系统升级等理由,通过任何方式发送给您退款链接。请您谨防钓鱼链接或诈骗电话!</p>
+							<div class="bottom" >
+								<p style="padding-bottom:16px;color: #2CB7CA; font-size: 14px;line-height: 24px;">发票信息</p>
+								<p class="item">
+									<span class="label">发票类型:</span>
+									<span class="value">%s</span>
+								</p>
+								<p class="item">
+									<span class="label">发票内容:</span>
+									<span class="value">信息技术服务-技术服务费</span>
+								</p>`
+
+	emailHtml_gs = `<p class="item">
+						<span class="label">公司名称:</span>
+						<span class="value">%s</span>
+					</p>
+					<p class="item">
+						<span class="label">纳税人识别号:</span>
+						<span class="value">%s</span>
+					</p>
+					<p class="item">
+						<span class="label">联系电话:</span>
+						<span class="value">%s</span>
+					</p>
+					<p class="item">
+						<span class="label">电子邮箱:</span>
+						<span class="value">%s</span>
+					</p>
+				</div>
+			</div>
+		</div>
+		</body>
+		</html>`
+
+	emailHtml_gr = `<p class="item">
+						<span class="label">联系电话:</span>
+						<span class="value">%s</span>
+					</p>
+					<p class="item">
+						<span class="label">电子邮箱:</span>
+						<span class="value">%s</span>
+					</p>
+				</div>
+			</div>
+		</div>
+		</body>
+		</html>`
+)
+
+func init() {
+	ctx := context.Background()
+	err := g.Cfg().MustGet(ctx, "mailConfig").Struct(&mailConfigArr)
+	if err != nil {
+		g.Log().Panic(ctx, "初始化邮件配置异常")
+	}
+	for _, v := range mailConfigArr {
+		GmailAuth = append(GmailAuth, &mail.GmailAuth{
+			SmtpHost: v.Addr,
+			SmtpPort: v.Port,
+			User:     v.User,
+			Pwd:      v.Pwd,
+		})
+	}
+	//SendMail(ctx, "wangkaiyue@topnet.net.cn", "<h1>Hello world</h1>", "测试邮件")
+}
+
+func SendMail(ctx context.Context, targetMail, html, title string) error {
+	for k, v := range GmailAuth {
+		if mail.GSendMail("剑鱼标讯", targetMail, "", "", title, html, "", "", v) {
+			g.Log().Infof(ctx, "发送邮件成功%s", targetMail)
+			break
+		}
+		if k < len(GmailAuth)-1 {
+			g.Log().Infof(ctx, "发送邮件失败%s!3s后使用其他邮箱尝试", targetMail)
+		} else {
+			return gerror.Newf("发送邮件失败%s![final]", targetMail)
+		}
+		time.Sleep(time.Second * 3)
+	}
+	return nil
+}
+
+func SendInvoiceSuccessMail(ctx context.Context, orderCode string) error {
+	var (
+		queryItem = "order_code"
+		err       error
+		record    invoiceRecord
+	)
+
+	if strings.Contains(orderCode, "xx") {
+		//自助开票
+		queryItem = "only_Identifying"
+	}
+	res, err := g.DB().GetOne(ctx, fmt.Sprintf("SELECT invoice_type,mail,taxpayer_identnum,company_name,phone,url FROM invoice WHEWE %s = ? ", queryItem), orderCode)
+	if err != nil {
+		return gerror.Wrapf(err, "未查询到订单发票信息 %s:%s", queryItem, orderCode)
+	}
+
+	if err = res.Struct(record); err != nil {
+		return gerror.Wrapf(err, "格式化发票信息异常")
+	}
+
+	if record.Mail == "" {
+		return nil
+	}
+
+	return SendMail(ctx, record.Mail, record.GetMailHtmlContext(), "电子发票")
+}
+
+func (ir *invoiceRecord) GetMailHtmlContext() string {
+	switch ir.InvoiceType {
+	case "单位":
+		return fmt.Sprintf(emailHtml_header+emailHtml_gs, ir.OrderCode, "电子普通发票", fmt.Sprintf("<a href=\"%s\" download class=\"download\">下载发票</a>", ir.Url), "电子普通发票", ir.CompanyName, ir.TaxpayerIdentnum, ir.Phone, ir.Mail)
+	case "个人":
+		return fmt.Sprintf(emailHtml_gr+emailHtml_gr, ir.OrderCode, "电子普通发票", fmt.Sprintf("<a href=\"%s\" download class=\"download\">下载发票</a>", ir.Url), "电子普通发票", ir.Phone, ir.Mail)
+	}
+	return ""
+}

+ 2 - 1
internal/service/tripartite/login.go

@@ -1,6 +1,7 @@
 package tripartite
 
 import (
+	"ElectronicInvoice/util"
 	"context"
 	"github.com/gogf/gf/v2/errors/gerror"
 	"github.com/gogf/gf/v2/frame/g"
@@ -46,7 +47,7 @@ func Login() error {
 	}
 
 	if loginType == 1 { //扫码登录
-		if err := SendQrImage2ChatBot(d.Ewm); err != nil {
+		if err := util.SendQrImage2ChatBot(d.Ewm); err != nil {
 			return gerror.Wrap(err, "发送企业微信登录消息异常")
 		}
 	} else {

+ 41 - 35
internal/service/tripartiteAuth.go → internal/service/tripartiteCommon.go

@@ -28,41 +28,6 @@ func createTripartite() *TripartiteAuth {
 	return t
 }
 
-// GetToken 获取token
-func (t *TripartiteAuth) GetToken(reload ...bool) (string, error) {
-	var (
-		ctx = context.Background()
-	)
-	type (
-		tokenResData struct {
-			Token     string `json:"token"`
-			ExpiresIn int    `json:"expiresIn"` //一天有效期
-		}
-	)
-	if time.Now().Before(t.effectiveTime) && t.token != "" && len(reload) == 0 {
-		return t.token, nil
-	}
-	err := CommonDoPost("/authority_token/getToken",
-		g.MapStrStr{"Content-Type": "application/x-www-form-urlencoded"},
-		g.MapStrAny{
-			"client_id":     g.Cfg().MustGet(ctx, "TripartiteAuth.clientId"),
-			"client_secret": g.Cfg().MustGet(ctx, "TripartiteAuth.clientSecret"),
-		},
-		func(i interface{}) error {
-			var d tokenResData
-			if err := gconv.Struct(i, &d); err != nil {
-				return err
-			}
-			t.token = d.Token
-			t.effectiveTime = time.Now().Add(time.Second * time.Duration(d.ExpiresIn))
-			return nil
-		})
-	if err != nil {
-		return "", err
-	}
-	return t.token, nil
-}
-
 // GetFormHeaderWithToken 携带token的form请求头
 func (t *TripartiteAuth) GetFormHeaderWithToken() map[string]string {
 	token, err := t.GetToken()
@@ -119,5 +84,46 @@ func CommonDoPost(url string, header map[string]string, param map[string]interfa
 		}
 		return nil
 	}
+
+	switch commonRes.Code {
+	case 202: //需要重新登录
+		JyInvoiceManager.Login = false
+		return consts.LoginOutErr
+	}
 	return fmt.Errorf("CommonDoPost:%s \nErrorCode:%d Msg:%s", url, commonRes.Code, commonRes.Msg)
 }
+
+// GetToken 获取token
+func (t *TripartiteAuth) GetToken(reload ...bool) (string, error) {
+	var (
+		ctx = context.Background()
+	)
+	type (
+		tokenResData struct {
+			Token     string `json:"token"`
+			ExpiresIn int    `json:"expiresIn"` //一天有效期
+		}
+	)
+	if time.Now().Before(t.effectiveTime) && t.token != "" && len(reload) == 0 {
+		return t.token, nil
+	}
+	err := CommonDoPost("/authority_token/getToken",
+		g.MapStrStr{"Content-Type": "application/x-www-form-urlencoded"},
+		g.MapStrAny{
+			"client_id":     g.Cfg().MustGet(ctx, "tripartite.clientId"),
+			"client_secret": g.Cfg().MustGet(ctx, "tripartite.clientSecret"),
+		},
+		func(i interface{}) error {
+			var d tokenResData
+			if err := gconv.Struct(i, &d); err != nil {
+				return err
+			}
+			t.token = d.Token
+			t.effectiveTime = time.Now().Add(time.Second * time.Duration(d.ExpiresIn))
+			return nil
+		})
+	if err != nil {
+		return "", err
+	}
+	return t.token, nil
+}

+ 38 - 3
internal/service/tripartiteInvoice.go

@@ -8,6 +8,7 @@ import (
 )
 
 type (
+	// 开发票
 	makeInvoiceAllParam struct {
 		TaxNum string            `json:"taxNum"` //企业税号*
 		Tel    string            `json:"tel"`    //登录电子税局手机号或身份证号*
@@ -36,15 +37,27 @@ type (
 		Sl       string `json:"sl"`        //数量
 		Tsaxrate string `json:"taxrate"`   //税率
 	}
+
+	//红冲
+	makeRedInvoiceAllParam struct {
+		TaxNum string               `json:"taxNum"` //企业税号*
+		Tel    string               `json:"tel"`    //登录电子税局手机号或身份证号*
+		Data   []MakeRedInvoiceData `json:"data"`
+	}
+
+	MakeRedInvoiceData struct {
+		Num  string `json:"num"`  //票类* 1 增值税专用发票;2 普通发票
+		Date string `json:"date"` //购买方名称* 人名
+	}
 )
 
 // MakeSingleInvoice 开单张发票
 func (t *TripartiteAuth) MakeSingleInvoice(param MakeInvoiceData) (err error) {
-	return t.MakeInvoices([]MakeInvoiceData{param})
+	return t.makeInvoices([]MakeInvoiceData{param})
 }
 
-// MakeInvoices 开多张发票
-func (t *TripartiteAuth) MakeInvoices(invoices []MakeInvoiceData) (err error) {
+// MakeInvoices 开多张发票(请勿使用,阻塞任务)
+func (t *TripartiteAuth) makeInvoices(invoices []MakeInvoiceData) (err error) {
 	var (
 		ctx   = context.Background()
 		param = makeInvoiceAllParam{
@@ -63,3 +76,25 @@ func (t *TripartiteAuth) MakeInvoices(invoices []MakeInvoiceData) (err error) {
 	}
 	return nil
 }
+
+// MakeSingleRedInvoice 红冲发票
+func (t *TripartiteAuth) MakeSingleRedInvoice(red MakeRedInvoiceData) (err error) {
+	var (
+		ctx   = context.Background()
+		param = makeRedInvoiceAllParam{
+			TaxNum: g.Cfg().MustGet(ctx, "company.taxNum").String(),
+			Tel:    g.Cfg().MustGet(ctx, "company.tel").String(),
+			Data:   []MakeRedInvoiceData{red},
+		}
+	)
+	err = CommonDoPost("/index_index/makeRedC",
+		t.GetJsonHeaderWithToken(),
+		gconv.Map(gconv.String(param)),
+		nil,
+	)
+	if err != nil {
+		err = gerror.Wrap(err, "开票红冲异常")
+		return
+	}
+	return nil
+}

+ 131 - 0
internal/service/tripartiteLogin.go

@@ -0,0 +1,131 @@
+package service
+
+import (
+	"ElectronicInvoice/internal/consts"
+	"ElectronicInvoice/util"
+	"context"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/util/gconv"
+	"time"
+)
+
+// Login 登录系统
+func (t *TripartiteAuth) Login() error {
+	type (
+		loginFirstRes struct {
+			Ewm                string `json:"ewm"`                //扫码二维码
+			IsVerificationCode int    `json:"isVerificationCode"` //是否需要输入验证码 1需要 2不需要
+		}
+	)
+	var (
+		ctx       = context.Background()
+		d         loginFirstRes
+		loginType = g.Cfg().MustGet(ctx, "loginType", 0).Int() //0 短信登录 1 扫码登录
+	)
+
+	//授权登录
+	err := CommonDoPost("/index_index/startPort",
+		t.GetFormHeaderWithToken(),
+		g.MapStrAny{
+			"taxNum":     g.Cfg().MustGet(ctx, "company.taxNum"),
+			"tel":        g.Cfg().MustGet(ctx, "company.tel"),
+			"password":   g.Cfg().MustGet(ctx, "company.password"),
+			"login_type": loginType,
+			"parameter":  "cccc",
+		},
+		func(i interface{}) error {
+			if err := gconv.Struct(i, &d); err != nil {
+				return err
+			}
+			return nil
+		})
+	if err != nil {
+		return gerror.Wrap(err, "授权登录异常")
+	}
+
+	if loginType == 1 { //扫码登录
+		if err := util.SendQrImage2ChatBot(d.Ewm); err != nil {
+			return gerror.Wrap(err, "发送企业微信登录消息异常")
+		}
+		//ToDo be developed
+		// 等待扫完完成回调 默认扫码成功
+		time.Sleep(time.Minute * 5)
+	} else {
+		//TODO 发送消息 取填写验证码
+		//长期授权登录
+		if err := t.LoginAfterKeepOnline(); err != nil {
+			return gerror.Wrap(err, "短信验证失败")
+		}
+	}
+	JyInvoiceManager.Login = true
+	return nil
+}
+
+// LoginAfterKeepOnline 长期登录授权接口
+func (t *TripartiteAuth) LoginAfterKeepOnline() error {
+	var (
+		ctx = context.Background()
+		yzm string
+	)
+	g.Log().Infof(ctx, "等待接收登录验证码")
+	select {
+	case <-time.After(time.Minute * 5):
+		g.Log().Infof(ctx, "已接接收登录验证码接收超时")
+		return consts.AuthTimeOut
+	case yzm = <-JyInvoiceManager.phoneCode:
+	}
+
+	g.Log().Infof(ctx, "已接接收登录验证码 %s", yzm)
+	err := CommonDoPost("/index_index/onLine",
+		t.GetFormHeaderWithToken(),
+		g.MapStrAny{
+			"taxNum": g.Cfg().MustGet(ctx, "company.taxNum"),
+			"tel":    g.Cfg().MustGet(ctx, "company.tel"),
+			"yzm":    yzm, //手机短信验证码怎么获取??
+		},
+		nil)
+	if err != nil {
+		return gerror.Wrap(err, "授权登录异常")
+	}
+	return nil
+}
+
+// VerifyLogin 验证登录
+func (t *TripartiteAuth) VerifyLogin() error {
+	var (
+		ctx = context.Background()
+	)
+	err := CommonDoPost("/index_index/verifyLogin",
+		t.GetFormHeaderWithToken(),
+		g.MapStrAny{
+			"taxNum": g.Cfg().MustGet(ctx, "company.taxNum"),
+			"tel":    g.Cfg().MustGet(ctx, "company.tel"),
+		},
+		nil)
+	if err != nil {
+		return gerror.Wrap(err, "授权登录异常")
+	}
+	return err
+}
+
+// SyncData 验证登录
+func (t *TripartiteAuth) SyncData() error {
+	var (
+		ctx = context.Background()
+	)
+	err := CommonDoPost("/index_index/syncData",
+		t.GetFormHeaderWithToken(),
+		g.MapStrAny{
+			"taxNum":     g.Cfg().MustGet(ctx, "company.taxNum"),
+			"tel":        g.Cfg().MustGet(ctx, "company.tel"),
+			"start_time": "2024-01-01",
+			"end_time":   "2024-05-01",
+			"total":      10,
+		},
+		nil)
+	if err != nil {
+		return gerror.Wrap(err, "Demo")
+	}
+	return err
+}

+ 36 - 0
util/pdf.go

@@ -0,0 +1,36 @@
+package util
+
+import (
+	"context"
+	"encoding/base64"
+	"fmt"
+	"github.com/gogf/gf/v2/errors/gerror"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gfile"
+	"time"
+)
+
+func SavePdfFile(ctx context.Context, num, pdfBase64 string) (filePath string, err error) {
+	var (
+		rootDir      = g.Cfg().MustGet(ctx, "pdfFilePathRootDir", "/out").String()
+		pathPrefix   = g.Cfg().MustGet(ctx, "pdfFilePathPrefix", "/invoice").String()
+		webDoMain    = g.Cfg().MustGet(ctx, "pdfFilePathDomain", "https://www.jianyu360.com").String()
+		now          = time.Now()
+		requestPath  = fmt.Sprintf("/%s/%s/%s.pdf", pathPrefix, now.Format("2006-01-02"), num)
+		fileFullPath = fmt.Sprintf("%s%s", rootDir, requestPath)
+	)
+
+	data, err := base64.StdEncoding.DecodeString(pdfBase64)
+	if err != nil {
+		return "", gerror.Wrap(err, "解析文件base64编码失败")
+	}
+
+	pdfFile, err := gfile.Create(fileFullPath)
+	if err != nil {
+		return "", gerror.Wrap(err, "创建pdf文件异常")
+	}
+	if _, err = pdfFile.Write(data); err != nil {
+		return "", gerror.Wrap(err, "pdf文件写入异常")
+	}
+	return fmt.Sprintf("%s%s", webDoMain, requestPath), nil
+}