commit 511d889557f5ecd0bbb445da1704c2433bafef2c Author: Eigeen Date: Tue Dec 20 21:45:52 2022 +0800 完成用户名更新,粉丝数获取的基础功能 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66fd13c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..07731f0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/spider-scheduler.iml b/.idea/spider-scheduler.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/spider-scheduler.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ed33232 --- /dev/null +++ b/go.mod @@ -0,0 +1,33 @@ +module github.com/eigeen/furryboard/spider-scheduler + +go 1.19 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/antonfisher/nested-logrus-formatter v1.3.1 + github.com/mcuadros/go-defaults v1.2.0 + github.com/sirupsen/logrus v1.9.0 + google.golang.org/grpc v1.51.0 + gorm.io/driver/postgres v1.4.5 + gorm.io/gorm v1.24.2 +) + +require ( + github.com/golang/protobuf v1.5.2 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.13.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.12.0 // indirect + github.com/jackc/pgx/v4 v4.17.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.4 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/text v0.4.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + google.golang.org/protobuf v1.27.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8f7810a --- /dev/null +++ b/go.sum @@ -0,0 +1,276 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= +github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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.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/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= +github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= +github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= +github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= +github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= +github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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.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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +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/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190423024810-112230192c58/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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/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.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +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.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc= +gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg= +gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0= +gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= +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= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/main.go b/main.go new file mode 100644 index 0000000..4d9f549 --- /dev/null +++ b/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "github.com/eigeen/furryboard/spider-scheduler/pkg/conf" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" +) + +func main() { + log.InitLogger(false) + conf.InitConfig("config.toml") +} diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go new file mode 100644 index 0000000..3de2d10 --- /dev/null +++ b/pkg/conf/conf.go @@ -0,0 +1,79 @@ +package conf + +import ( + "encoding/json" + "github.com/BurntSushi/toml" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "github.com/eigeen/furryboard/spider-scheduler/pkg/util" + "github.com/mcuadros/go-defaults" + "os" +) + +type Config struct { + Common Common `toml:"common"` + Database Database `toml:"database"` + SpiderCore SpiderCore `toml:"spider_core"` +} + +type Common struct { +} + +type Database struct { + Host string `toml:"host" default:"localhost"` + Port uint16 `toml:"port" default:"5432"` + User string `toml:"user" default:"user"` + Password string `toml:"password" default:"password"` + DB string `toml:"db" default:"furryboard"` +} + +type SpiderCore struct { + Host string `toml:"host" default:"localhost"` + Port uint16 `toml:"port" default:"9996"` +} + +var ( + Conf *Config +) + +// 释放默认配置文件 +func releaseConfig(file string, config *Config) { + f, err := util.CreateNestedFile(file) + if err != nil { + log.Logger().Fatalf("创建默认配置文件失败: %s", err.Error()) + } + + encoder := toml.NewEncoder(f) + err = encoder.Encode(config) + if err != nil { + log.Logger().Fatalf("创建默认配置文件失败: %s", err.Error()) + } +} + +func InitConfig(file string) { + Conf = &Config{} + defaults.SetDefaults(Conf) + + if util.NotExists(file) { + // 默认配置文件不存在则创建 + if file == "config.toml" { + releaseConfig(file, Conf) + log.Logger().Infof("已创建默认配置文件: %s,请修改配置文件内容后重新启动", file) + os.Exit(0) + } else { + log.Logger().Fatalf("配置文件不存在: %s", file) + } + } + + // 解析配置文件 + _, err := toml.DecodeFile(file, &Conf) + if err != nil { + log.Logger().Fatalf("配置文件解析出错: %s", err.Error()) + } + + // 配置预检查 + + jsonConfig, err := json.Marshal(Conf) + if err == nil { + log.Logger().Debugf("配置文件已加载: %s", string(jsonConfig)) + } +} diff --git a/pkg/dao/dao.go b/pkg/dao/dao.go new file mode 100644 index 0000000..34ead54 --- /dev/null +++ b/pkg/dao/dao.go @@ -0,0 +1,77 @@ +package dao + +import ( + "fmt" + "github.com/eigeen/furryboard/spider-scheduler/pkg/conf" + "github.com/eigeen/furryboard/spider-scheduler/pkg/dao/model" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "github.com/sirupsen/logrus" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "sync" + "time" +) + +var ( + once sync.Once + db *gorm.DB +) + +func DB() *gorm.DB { + once.Do(initDB) + return db + +} + +type LogrusWriter struct { + log *logrus.Logger +} + +func (w *LogrusWriter) Printf(format string, v ...interface{}) { + logStr := fmt.Sprintf(format, v...) + w.log.WithField("method", "GORM").Warn(logStr) +} + +func NewLogger() *LogrusWriter { + return &LogrusWriter{log: log.Logger()} +} + +func initDB() { + var err error + dsn := fmt.Sprintf( + "host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai", + conf.Conf.Database.Host, + conf.Conf.Database.User, + conf.Conf.Database.Password, + conf.Conf.Database.DB, + conf.Conf.Database.Port, + ) + + // >= 1s SQL慢查询 + slowLogger := logger.New(NewLogger(), logger.Config{ + SlowThreshold: time.Second * 1, + LogLevel: logger.Warn, + }) + db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{ + Logger: slowLogger, + }) + if err != nil { + log.Logger().Fatal("创建数据库连接失败:", err) + } + + autoMigrate() +} + +func autoMigrate() { + var err error + err = db.AutoMigrate(&model.Changelog{}) + err = db.AutoMigrate(&model.Fans{}) + if err != nil { + return + } + + if err != nil { + log.Logger().Fatal("关联数据表失败:", err) + } +} diff --git a/pkg/dao/model/changelog.go b/pkg/dao/model/changelog.go new file mode 100644 index 0000000..1cd43fd --- /dev/null +++ b/pkg/dao/model/changelog.go @@ -0,0 +1,10 @@ +package model + +import "gorm.io/gorm" + +type Changelog struct { + gorm.Model + Operation string `gorm:"index;not null;default:''"` + Target string `gorm:"index;not null;default:''"` + Result string `gorm:"not null;default:''"` +} diff --git a/pkg/dao/model/fans.go b/pkg/dao/model/fans.go new file mode 100644 index 0000000..dff3f1b --- /dev/null +++ b/pkg/dao/model/fans.go @@ -0,0 +1,13 @@ +package model + +import "gorm.io/gorm" + +type Fans struct { + gorm.Model + UID uint `gorm:"index;not null"` + Fans uint `gorm:"index;not null;default:0"` +} + +func (Fans) TableName() string { + return "fans" +} diff --git a/pkg/dao/model/furry.go b/pkg/dao/model/furry.go new file mode 100644 index 0000000..87871ff --- /dev/null +++ b/pkg/dao/model/furry.go @@ -0,0 +1,12 @@ +package model + +type Furry struct { + ID uint `gorm:"primaryKey"` + UID uint `gorm:"uniqueIndex;not null"` + Name string `gorm:"index;not null;default:''"` + Status int `gorm:"index;not null;default:0"` +} + +func (Furry) TableName() string { + return "furries" +} diff --git a/pkg/exception/base.go b/pkg/exception/base.go new file mode 100644 index 0000000..84c7909 --- /dev/null +++ b/pkg/exception/base.go @@ -0,0 +1,15 @@ +package exception + +import ( + "fmt" +) + +type BaseError struct { + Code int + Msg string + ShortMsg string +} + +func (err BaseError) Error() string { + return fmt.Sprintf("%d - 错误: %s - 原因:%s", err.Code, err.ShortMsg, err.Msg) +} diff --git a/pkg/exception/business.go b/pkg/exception/business.go new file mode 100644 index 0000000..9e679b0 --- /dev/null +++ b/pkg/exception/business.go @@ -0,0 +1,26 @@ +package exception + +func InternalError(msg string) BaseError { + shortMsg := "系统异常,请联系管理员" + return BaseError{Code: 500, Msg: msg, ShortMsg: shortMsg} +} + +func ErrFetchUserInfo(msg string) BaseError { + shortMsg := "用户信息获取失败" + return BaseError{Code: 5001, Msg: msg, ShortMsg: shortMsg} +} + +func ErrFetchFurries(msg string) BaseError { + shortMsg := "获取Furry列表失败" + return BaseError{Code: 5002, Msg: msg, ShortMsg: shortMsg} +} + +func ErrUserMismatch(msg string) BaseError { + shortMsg := "操作的两个用户不匹配" + return BaseError{Code: 5003, Msg: msg, ShortMsg: shortMsg} +} + +func ErrFetchStat(msg string) BaseError { + shortMsg := "获取用户统计信息失败" + return BaseError{Code: 5004, Msg: msg, ShortMsg: shortMsg} +} diff --git a/pkg/log/logger.go b/pkg/log/logger.go new file mode 100644 index 0000000..2ff93ed --- /dev/null +++ b/pkg/log/logger.go @@ -0,0 +1,32 @@ +package log + +import ( + nested "github.com/antonfisher/nested-logrus-formatter" + "github.com/sirupsen/logrus" +) + +var logger *logrus.Logger + +func Logger() *logrus.Logger { + return logger +} + +func InitLogger(debug bool) { + log := logrus.New() + log.SetFormatter(&nested.Formatter{ + FieldsOrder: []string{"method", "url", "statusCode", "spendTime"}, + HideKeys: true, + NoFieldsColors: true, + TimestampFormat: "2006-01-02 15:04:05.000", + }) + + var lvl logrus.Level + switch debug { + case true: + lvl = logrus.DebugLevel + case false: + lvl = logrus.InfoLevel + } + log.SetLevel(lvl) + logger = log +} diff --git a/pkg/logic/config.toml b/pkg/logic/config.toml new file mode 100644 index 0000000..b4d41be --- /dev/null +++ b/pkg/logic/config.toml @@ -0,0 +1,12 @@ +[common] + +[database] + host = "localhost" + port = 5432 + user = "user" + password = "password" + db = "furryboard" + +[spider_core] + host = "localhost" + port = 9101 diff --git a/pkg/logic/user.go b/pkg/logic/user.go new file mode 100644 index 0000000..d04e7a7 --- /dev/null +++ b/pkg/logic/user.go @@ -0,0 +1,242 @@ +package logic + +import ( + "context" + "encoding/json" + "fmt" + "github.com/eigeen/furryboard/spider-scheduler/pkg/dao" + "github.com/eigeen/furryboard/spider-scheduler/pkg/dao/model" + "github.com/eigeen/furryboard/spider-scheduler/pkg/exception" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "github.com/eigeen/furryboard/spider-scheduler/rpc" + "github.com/eigeen/furryboard/spider-scheduler/rpc/pb" + "gorm.io/gorm" + "strconv" + "sync" + "time" +) + +// GetUsers 分页获取用户信息(数据库) +func GetUsers(page, pageSize int) ([]*model.Furry, error) { + // 分页查询 + var furries []*model.Furry + tx := dao.DB().Offset((page - 1) * pageSize).Limit(pageSize).Find(&furries) + if tx.Error != nil { + return nil, exception.ErrFetchFurries("分页获取Furry列表时失败:" + tx.Error.Error()) + } + return furries, nil +} + +// GetUserInfoByUID 获取用户粉丝数 包括名称,性别,签名等 +func GetUserInfoByUID(uid uint) (*pb.InfoReply_Data, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + info, err := rpc.SpiderCore().GetUserBasicInfo(ctx, &pb.InfoRequest{Uid: uint64(uid)}) + if err != nil { + return nil, exception.InternalError("获取用户信息失败:" + err.Error()) + } + if info.Code != 200 { + if info.Msg == "" { + info.Msg = "Unknown" + } + return nil, exception.ErrFetchUserInfo( + fmt.Sprintf("用户[%d]基础信息获取失败:%s(Code: %d)", uid, info.Msg, info.Code)) + } + return info.Data, nil +} + +// BatchGetUserInfo 批量获取用户基础信息 +func BatchGetUserInfo(userIds *[]uint) []*pb.InfoReply_Data { + poolSize := 8 + sem := make(chan struct{}, poolSize) + defer close(sem) + wg := sync.WaitGroup{} + + results := make(chan *pb.InfoReply_Data, len(*userIds)) + defer close(results) + for _, uid := range *userIds { + sem <- struct{}{} + wg.Add(1) + go func(uid uint) { + <-sem + defer wg.Done() + info, err := GetUserInfoByUID(uid) + if err != nil { + log.Logger().Warnf("用户[%d]基础信息获取失败:%s。已忽略", uid, err) + return + } + results <- info + log.Logger().Debugf("获取用户[%d]基础信息成功", uid) + }(uid) + } + wg.Wait() + + s := make([]*pb.InfoReply_Data, 0) + resultLen := len(results) + for i := 0; i < resultLen; i++ { + s = append(s, <-results) + } + return s +} + +func UpdateUserInfo(users []*model.Furry, infos []*pb.InfoReply_Data) (int, error) { + // 以UID为索引转换为map + infoMap := make(map[uint]*pb.InfoReply_Data) + for _, i := range infos { + infoMap[uint(i.Mid)] = i + } + // 检测变化 + changelogs, changes, err := createChangelogs(users, infos) + if err != nil { + return 0, err + } + if len(changelogs) == 0 { // 无变化 + return 0, nil + } + // 数据库事务 + err = dao.DB().Transaction(func(tx *gorm.DB) error { + for _, changed := range changes { + // 更新furries表 + if err = tx.Model(&model.Furry{ID: changed.ID}).Updates(changed).Error; err != nil { + return err + } + } + for _, changelog := range changelogs { + // 插入changelogs表 + if err = tx.Create(changelog).Error; err != nil { + return err + } + } + return nil + }) + if err != nil { + return 0, err + } + return len(changelogs), nil +} + +func userInfoDiff(user *model.Furry, info *pb.InfoReply_Data) (*model.Changelog, *model.Furry, error) { + if uint64(user.UID) != info.Mid { + return nil, nil, exception.ErrUserMismatch(fmt.Sprintf("对比用户ID不匹配:%d和%d", user.UID, info.Mid)) + } + changelog := model.Changelog{ + Operation: "UpdateName", + Target: strconv.FormatUint(uint64(user.UID), 10), + Result: "", + } + updated := model.Furry{ + ID: user.ID, + } + if user.Name != info.Name { + result, _ := json.Marshal(map[string]string{"previous_name": user.Name, "new_name": info.Name}) + changelog.Result = string(result) + updated.Name = info.Name + return &changelog, &updated, nil + } else { + return nil, nil, nil + } +} + +func createChangelogs(users []*model.Furry, infos []*pb.InfoReply_Data) ([]*model.Changelog, []*model.Furry, error) { + // 以UID为索引转换为map + infoMap := make(map[uint]*pb.InfoReply_Data) + for _, i := range infos { + infoMap[uint(i.Mid)] = i + } + // 检测变化 + changelogs := make([]*model.Changelog, 0) + changes := make([]*model.Furry, 0) + for _, u := range users { + if info, ok := infoMap[u.UID]; ok { + changelog, changed, err := userInfoDiff(u, info) + if err != nil { + return nil, nil, err + } + if changed == nil { // 无变化 + continue + } + changes = append(changes, changed) + changelogs = append(changelogs, changelog) + } + } + return changelogs, changes, nil +} + +// GetStatByUID 获取用户粉丝数 +func GetStatByUID(uid uint) (*pb.StatReply_Data, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + info, err := rpc.SpiderCore().GetUserStat(ctx, &pb.StatRequest{Uid: uint64(uid)}) + if err != nil { + return nil, exception.InternalError("获取用户信息失败:" + err.Error()) + } + if info.Code != 200 { + if info.Msg == "" { + info.Msg = "Unknown" + } + return nil, exception.ErrFetchStat( + fmt.Sprintf("用户[%d]粉丝数获取失败:%s(Code: %d)", uid, info.Msg, info.Code)) + } + return info.Data, nil +} + +// BatchGetUserStat 批量获取用户粉丝数 +func BatchGetUserStat(uids *[]uint) []*pb.StatReply_Data { + poolSize := 8 + sem := make(chan struct{}, poolSize) + defer close(sem) + wg := sync.WaitGroup{} + + results := make(chan *pb.StatReply_Data, len(*uids)) + defer close(results) + for _, uid := range *uids { + sem <- struct{}{} + wg.Add(1) + go func(uid uint) { + <-sem + defer wg.Done() + stat, err := GetStatByUID(uid) + if err != nil { + log.Logger().Warnf("用户[%d]粉丝数获取失败:%s。已忽略", uid, err) + return + } + results <- stat + log.Logger().Debugf("获取用户[%d]粉丝数成功", uid) + }(uid) + } + wg.Wait() + + s := make([]*pb.StatReply_Data, 0) + resultLen := len(results) + for i := 0; i < resultLen; i++ { + s = append(s, <-results) + } + return s +} + +func UpdateFansToDB(stats []*pb.StatReply_Data) (int, error) { + var err error + // 抽取必要字段转为数据库模型 + var models []*model.Fans + for _, stat := range stats { + models = append(models, &model.Fans{ + UID: uint(stat.Mid), + Fans: uint(stat.Follower), + }) + } + + // 数据库事务 + err = dao.DB().Transaction(func(tx *gorm.DB) error { + for _, m := range models { + if err = tx.Create(m).Error; err != nil { + return err + } + } + return nil + }) + if err != nil { + return 0, err + } + return len(stats), nil + +} diff --git a/pkg/logic/user_test.go b/pkg/logic/user_test.go new file mode 100644 index 0000000..53830f4 --- /dev/null +++ b/pkg/logic/user_test.go @@ -0,0 +1,33 @@ +package logic + +import ( + "github.com/eigeen/furryboard/spider-scheduler/pkg/conf" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "testing" +) + +func BeforeTesting() { + log.InitLogger(false) + conf.InitConfig("config.toml") +} + +func TestGetUserInfoByUID(t *testing.T) { + BeforeTesting() + info, err := GetUserInfoByUID(686127) + if err != nil { + t.Error(err) + return + } + + t.Log(info) +} + +func TestBatchGetUserInfo(t *testing.T) { + BeforeTesting() + userIds := []uint{1635354787, 10504485, 272460888, 474481910, 82191626} + infos := BatchGetUserInfo(&userIds) + if len(infos) != len(userIds) { + t.Errorf("结果长度不正确:应为%d,实际为%d", len(userIds), len(infos)) + return + } +} diff --git a/pkg/task/config.toml b/pkg/task/config.toml new file mode 100644 index 0000000..724e804 --- /dev/null +++ b/pkg/task/config.toml @@ -0,0 +1,12 @@ +[common] + +[database] + host = "localhost" + port = 5432 + user = "postgres" + password = "postrootpwd" + db = "furryboard" + +[spider_core] + host = "localhost" + port = 9101 diff --git a/pkg/task/user.go b/pkg/task/user.go new file mode 100644 index 0000000..29ac31c --- /dev/null +++ b/pkg/task/user.go @@ -0,0 +1,103 @@ +package task + +import ( + "github.com/eigeen/furryboard/spider-scheduler/pkg/dao" + "github.com/eigeen/furryboard/spider-scheduler/pkg/dao/model" + "github.com/eigeen/furryboard/spider-scheduler/pkg/exception" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "github.com/eigeen/furryboard/spider-scheduler/pkg/logic" + "math" + "time" +) + +// UpdateUserInfo 更新数据库内用户信息 +// 会对原始表进行更改,并将更改记录写入changelogs +func UpdateUserInfo() { + // 预查询数量 + // TODO: 预查询数量和分页策略功能可复用 + var count int64 + tx := dao.DB().Model(&model.Furry{}).Count(&count) + if tx.Error != nil { + err := exception.ErrFetchFurries("获取furries数量失败:" + tx.Error.Error()) + log.Logger().Errorf("获取furries数量失败:%s", err) + return + } + + pageSize := 32 + maxPage := int(math.Ceil(float64(count) / float64(pageSize))) + for page := 0; page <= maxPage; page++ { + // 获取目标用户列表(分页) + users, err := logic.GetUsers(page, pageSize) + if err != nil { + log.Logger().Errorf("获取用户列表时发生错误:%s", err) + continue + } + // 已获取完毕 + if users == nil { + return + } + // 抽取uid列表 + var uids []uint + for _, user := range users { + uids = append(uids, user.UID) + } + // 通过API获取用户信息 + infos := logic.BatchGetUserInfo(&uids) + + // 检查更新内容,更新furries表,插入changelogs(使用事务) + c, err := logic.UpdateUserInfo(users, infos) + if err != nil { + log.Logger().Errorf("更新数据时发生错误:%s", err) + continue + } + log.Logger().Infof("成功更新了%d个用户信息", c) + // delay + time.Sleep(2 * time.Second) + } +} + +func UpdateFans() { + // TODO: 与UpdateUserInfo有大量重复,可抽离复用代码 + // 预查询数量 + var count int64 + tx := dao.DB().Model(&model.Furry{}).Count(&count) + if tx.Error != nil { + err := exception.ErrFetchFurries("获取furries数量失败:" + tx.Error.Error()) + log.Logger().Errorf("获取furries数量失败:%s", err) + return + } + + pageSize := 4 + maxPage := int(math.Ceil(float64(count) / float64(pageSize))) + for page := 0; page <= maxPage; page++ { + // 获取目标用户列表(分页) + users, err := logic.GetUsers(page, pageSize) + if err != nil { + log.Logger().Errorf("获取用户列表时发生错误:%s", err) + continue + } + // 已获取完毕 + if users == nil { + return + } + // 抽取uid列表 + var uids []uint + for _, user := range users { + uids = append(uids, user.UID) + } + // 通过API获取用户统计信息 + stats := logic.BatchGetUserStat(&uids) + // 插入表 + c, err := logic.UpdateFansToDB(stats) + if err != nil { + log.Logger().Errorf("更新粉丝数量时发生错误:%s", err) + return + } + log.Logger().Infof("成功更新了%d个用户粉丝数", c) + time.Sleep(2 * time.Second) + } +} + +func UpdateFansAndInfo() { + +} diff --git a/pkg/task/user_test.go b/pkg/task/user_test.go new file mode 100644 index 0000000..b1b26c3 --- /dev/null +++ b/pkg/task/user_test.go @@ -0,0 +1,22 @@ +package task + +import ( + "github.com/eigeen/furryboard/spider-scheduler/pkg/conf" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "testing" +) + +func BeforeTesting() { + log.InitLogger(false) + conf.InitConfig("config.toml") +} + +func TestUpdateUserInfo(t *testing.T) { + BeforeTesting() + UpdateUserInfo() +} + +func TestUpdateFans(t *testing.T) { + BeforeTesting() + UpdateFans() +} diff --git a/pkg/util/file.go b/pkg/util/file.go new file mode 100644 index 0000000..017be57 --- /dev/null +++ b/pkg/util/file.go @@ -0,0 +1,30 @@ +package util + +import ( + "os" + "path/filepath" +) + +func Exists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +func NotExists(name string) bool { + return !Exists(name) +} + +func CreateNestedFile(path string) (*os.File, error) { + basePath := filepath.Dir(path) + if !Exists(basePath) { + err := os.MkdirAll(basePath, 0700) + if err != nil { + return nil, err + } + } + return os.Create(path) +} diff --git a/pkg/util/rand.go b/pkg/util/rand.go new file mode 100644 index 0000000..a585cc7 --- /dev/null +++ b/pkg/util/rand.go @@ -0,0 +1,36 @@ +package util + +import ( + "math/rand" + "time" + "unsafe" +) + +const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" + +var src = rand.NewSource(time.Now().UnixNano()) + +const ( + // 6 bits to represent a letter index + letterIdBits = 6 + // All 1-bits as many as letterIdBits + letterIdMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdMax + } + if idx := int(cache & letterIdMask); idx < len(letters) { + b[i] = letters[idx] + i-- + } + cache >>= letterIdBits + remain-- + } + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/pkg/util/slice.go b/pkg/util/slice.go new file mode 100644 index 0000000..026cec9 --- /dev/null +++ b/pkg/util/slice.go @@ -0,0 +1,21 @@ +package util + +func InStringSlice(sl []string, ele string) bool { + slMap := convertStrSlice2Map(sl) + return inMap(slMap, ele) +} + +// ConvertStrSlice2Map 将字符串 slice 转为 map[string]struct{}。 +func convertStrSlice2Map(sl []string) map[string]struct{} { + set := make(map[string]struct{}, len(sl)) + for _, v := range sl { + set[v] = struct{}{} + } + return set +} + +// InMap 判断字符串是否在 map 中。 +func inMap(m map[string]struct{}, s string) bool { + _, ok := m[s] + return ok +} diff --git a/pkg/util/time.go b/pkg/util/time.go new file mode 100644 index 0000000..fb662d4 --- /dev/null +++ b/pkg/util/time.go @@ -0,0 +1,23 @@ +package util + +import "time" + +func ToSQLTimeFormat(t time.Time) string { + return t.Format("2006-01-02 15:04:05") +} + +func MustParseSQLTime(timeStr string) time.Time { + timeObj, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) + if err != nil { + panic(err) + } + return timeObj +} + +func IsSQLTimeFormat(timeStr string) bool { + _, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local) + if err != nil { + return false + } + return true +} diff --git a/rpc/pb/api.pb.go b/rpc/pb/api.pb.go new file mode 100644 index 0000000..acd391f --- /dev/null +++ b/rpc/pb/api.pb.go @@ -0,0 +1,754 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.8 +// source: api.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StatRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid uint64 `protobuf:"varint,1,opt,name=uid,proto3" json:"uid,omitempty"` +} + +func (x *StatRequest) Reset() { + *x = StatRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatRequest) ProtoMessage() {} + +func (x *StatRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatRequest.ProtoReflect.Descriptor instead. +func (*StatRequest) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{0} +} + +func (x *StatRequest) GetUid() uint64 { + if x != nil { + return x.Uid + } + return 0 +} + +type StatReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` + Data *StatReply_Data `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *StatReply) Reset() { + *x = StatReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatReply) ProtoMessage() {} + +func (x *StatReply) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatReply.ProtoReflect.Descriptor instead. +func (*StatReply) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{1} +} + +func (x *StatReply) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *StatReply) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *StatReply) GetData() *StatReply_Data { + if x != nil { + return x.Data + } + return nil +} + +type InfoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid uint64 `protobuf:"varint,1,opt,name=uid,proto3" json:"uid,omitempty"` +} + +func (x *InfoRequest) Reset() { + *x = InfoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoRequest) ProtoMessage() {} + +func (x *InfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoRequest.ProtoReflect.Descriptor instead. +func (*InfoRequest) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{2} +} + +func (x *InfoRequest) GetUid() uint64 { + if x != nil { + return x.Uid + } + return 0 +} + +type InfoReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` + Data *InfoReply_Data `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *InfoReply) Reset() { + *x = InfoReply{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InfoReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoReply) ProtoMessage() {} + +func (x *InfoReply) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoReply.ProtoReflect.Descriptor instead. +func (*InfoReply) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{3} +} + +func (x *InfoReply) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *InfoReply) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *InfoReply) GetData() *InfoReply_Data { + if x != nil { + return x.Data + } + return nil +} + +type StatReply_Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mid uint64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid,omitempty"` + Following uint32 `protobuf:"varint,2,opt,name=following,proto3" json:"following,omitempty"` + Whisper uint32 `protobuf:"varint,3,opt,name=whisper,proto3" json:"whisper,omitempty"` + Black uint32 `protobuf:"varint,4,opt,name=black,proto3" json:"black,omitempty"` + Follower uint32 `protobuf:"varint,5,opt,name=follower,proto3" json:"follower,omitempty"` +} + +func (x *StatReply_Data) Reset() { + *x = StatReply_Data{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatReply_Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatReply_Data) ProtoMessage() {} + +func (x *StatReply_Data) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatReply_Data.ProtoReflect.Descriptor instead. +func (*StatReply_Data) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{1, 0} +} + +func (x *StatReply_Data) GetMid() uint64 { + if x != nil { + return x.Mid + } + return 0 +} + +func (x *StatReply_Data) GetFollowing() uint32 { + if x != nil { + return x.Following + } + return 0 +} + +func (x *StatReply_Data) GetWhisper() uint32 { + if x != nil { + return x.Whisper + } + return 0 +} + +func (x *StatReply_Data) GetBlack() uint32 { + if x != nil { + return x.Black + } + return 0 +} + +func (x *StatReply_Data) GetFollower() uint32 { + if x != nil { + return x.Follower + } + return 0 +} + +type InfoReply_Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Mid uint64 `protobuf:"varint,1,opt,name=mid,proto3" json:"mid,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Sex string `protobuf:"bytes,3,opt,name=sex,proto3" json:"sex,omitempty"` + Face string `protobuf:"bytes,4,opt,name=face,proto3" json:"face,omitempty"` + FaceNft uint32 `protobuf:"varint,5,opt,name=face_nft,json=faceNft,proto3" json:"face_nft,omitempty"` + FaceNftType uint32 `protobuf:"varint,6,opt,name=face_nft_type,json=faceNftType,proto3" json:"face_nft_type,omitempty"` + Sign string `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` + Rank uint32 `protobuf:"varint,8,opt,name=rank,proto3" json:"rank,omitempty"` + Level uint32 `protobuf:"varint,9,opt,name=level,proto3" json:"level,omitempty"` + Jointime uint32 `protobuf:"varint,10,opt,name=jointime,proto3" json:"jointime,omitempty"` + Moral uint32 `protobuf:"varint,11,opt,name=moral,proto3" json:"moral,omitempty"` + Silence uint32 `protobuf:"varint,12,opt,name=silence,proto3" json:"silence,omitempty"` + Coins uint32 `protobuf:"varint,13,opt,name=coins,proto3" json:"coins,omitempty"` + FansBadge bool `protobuf:"varint,14,opt,name=fans_badge,json=fansBadge,proto3" json:"fans_badge,omitempty"` + IsFollowed bool `protobuf:"varint,21,opt,name=is_followed,json=isFollowed,proto3" json:"is_followed,omitempty"` + TopPhoto string `protobuf:"bytes,22,opt,name=top_photo,json=topPhoto,proto3" json:"top_photo,omitempty"` + Birthday string `protobuf:"bytes,26,opt,name=birthday,proto3" json:"birthday,omitempty"` + Tags []string `protobuf:"bytes,29,rep,name=tags,proto3" json:"tags,omitempty"` + IsSeniorMember uint32 `protobuf:"varint,31,opt,name=is_senior_member,json=isSeniorMember,proto3" json:"is_senior_member,omitempty"` + McnInfo string `protobuf:"bytes,32,opt,name=mcn_info,json=mcnInfo,proto3" json:"mcn_info,omitempty"` + GaiaResType uint32 `protobuf:"varint,33,opt,name=gaia_res_type,json=gaiaResType,proto3" json:"gaia_res_type,omitempty"` + GaiaData string `protobuf:"bytes,34,opt,name=gaia_data,json=gaiaData,proto3" json:"gaia_data,omitempty"` + IsRisk bool `protobuf:"varint,35,opt,name=is_risk,json=isRisk,proto3" json:"is_risk,omitempty"` +} + +func (x *InfoReply_Data) Reset() { + *x = InfoReply_Data{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InfoReply_Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoReply_Data) ProtoMessage() {} + +func (x *InfoReply_Data) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoReply_Data.ProtoReflect.Descriptor instead. +func (*InfoReply_Data) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *InfoReply_Data) GetMid() uint64 { + if x != nil { + return x.Mid + } + return 0 +} + +func (x *InfoReply_Data) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *InfoReply_Data) GetSex() string { + if x != nil { + return x.Sex + } + return "" +} + +func (x *InfoReply_Data) GetFace() string { + if x != nil { + return x.Face + } + return "" +} + +func (x *InfoReply_Data) GetFaceNft() uint32 { + if x != nil { + return x.FaceNft + } + return 0 +} + +func (x *InfoReply_Data) GetFaceNftType() uint32 { + if x != nil { + return x.FaceNftType + } + return 0 +} + +func (x *InfoReply_Data) GetSign() string { + if x != nil { + return x.Sign + } + return "" +} + +func (x *InfoReply_Data) GetRank() uint32 { + if x != nil { + return x.Rank + } + return 0 +} + +func (x *InfoReply_Data) GetLevel() uint32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *InfoReply_Data) GetJointime() uint32 { + if x != nil { + return x.Jointime + } + return 0 +} + +func (x *InfoReply_Data) GetMoral() uint32 { + if x != nil { + return x.Moral + } + return 0 +} + +func (x *InfoReply_Data) GetSilence() uint32 { + if x != nil { + return x.Silence + } + return 0 +} + +func (x *InfoReply_Data) GetCoins() uint32 { + if x != nil { + return x.Coins + } + return 0 +} + +func (x *InfoReply_Data) GetFansBadge() bool { + if x != nil { + return x.FansBadge + } + return false +} + +func (x *InfoReply_Data) GetIsFollowed() bool { + if x != nil { + return x.IsFollowed + } + return false +} + +func (x *InfoReply_Data) GetTopPhoto() string { + if x != nil { + return x.TopPhoto + } + return "" +} + +func (x *InfoReply_Data) GetBirthday() string { + if x != nil { + return x.Birthday + } + return "" +} + +func (x *InfoReply_Data) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + +func (x *InfoReply_Data) GetIsSeniorMember() uint32 { + if x != nil { + return x.IsSeniorMember + } + return 0 +} + +func (x *InfoReply_Data) GetMcnInfo() string { + if x != nil { + return x.McnInfo + } + return "" +} + +func (x *InfoReply_Data) GetGaiaResType() uint32 { + if x != nil { + return x.GaiaResType + } + return 0 +} + +func (x *InfoReply_Data) GetGaiaData() string { + if x != nil { + return x.GaiaData + } + return "" +} + +func (x *InfoReply_Data) GetIsRisk() bool { + if x != nil { + return x.IsRisk + } + return false +} + +var File_api_proto protoreflect.FileDescriptor + +var file_api_proto_rawDesc = []byte{ + 0x0a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, + 0x22, 0x1f, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x75, 0x69, + 0x64, 0x22, 0xdf, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x27, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x82, + 0x01, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6d, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x6f, 0x6c, + 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x66, 0x6f, + 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x68, 0x69, 0x73, 0x70, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x77, 0x68, 0x69, 0x73, 0x70, 0x65, + 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x72, 0x22, 0x1f, 0x0a, 0x0b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x03, 0x75, 0x69, 0x64, 0x22, 0xba, 0x05, 0x0a, 0x09, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x70, + 0x6c, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x27, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x1a, 0xdd, 0x04, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6d, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, + 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x66, 0x61, 0x63, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6e, + 0x66, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x66, + 0x74, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x66, 0x74, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x66, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x6e, + 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x72, 0x61, 0x6e, 0x6b, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x72, 0x61, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x6d, 0x6f, 0x72, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x61, 0x6e, 0x73, 0x5f, 0x62, 0x61, + 0x64, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x61, 0x6e, 0x73, 0x42, + 0x61, 0x64, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x73, 0x5f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x46, 0x6f, 0x6c, + 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x70, 0x5f, 0x70, 0x68, 0x6f, + 0x74, 0x6f, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x70, 0x50, 0x68, 0x6f, + 0x74, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x69, 0x72, 0x74, 0x68, 0x64, 0x61, 0x79, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x1d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x69, 0x6f, 0x72, 0x5f, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x69, 0x73, + 0x53, 0x65, 0x6e, 0x69, 0x6f, 0x72, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, + 0x6d, 0x63, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x63, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x67, 0x61, 0x69, 0x61, 0x5f, + 0x72, 0x65, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, + 0x67, 0x61, 0x69, 0x61, 0x52, 0x65, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, + 0x61, 0x69, 0x61, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x67, 0x61, 0x69, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x72, + 0x69, 0x73, 0x6b, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x52, 0x69, 0x73, + 0x6b, 0x32, 0x74, 0x0a, 0x07, 0x42, 0x69, 0x6c, 0x69, 0x41, 0x50, 0x49, 0x12, 0x31, 0x0a, 0x0b, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x12, 0x10, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x36, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x61, 0x73, 0x69, 0x63, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_api_proto_rawDescOnce sync.Once + file_api_proto_rawDescData = file_api_proto_rawDesc +) + +func file_api_proto_rawDescGZIP() []byte { + file_api_proto_rawDescOnce.Do(func() { + file_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_rawDescData) + }) + return file_api_proto_rawDescData +} + +var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_api_proto_goTypes = []interface{}{ + (*StatRequest)(nil), // 0: api.StatRequest + (*StatReply)(nil), // 1: api.StatReply + (*InfoRequest)(nil), // 2: api.InfoRequest + (*InfoReply)(nil), // 3: api.InfoReply + (*StatReply_Data)(nil), // 4: api.StatReply.Data + (*InfoReply_Data)(nil), // 5: api.InfoReply.Data +} +var file_api_proto_depIdxs = []int32{ + 4, // 0: api.StatReply.data:type_name -> api.StatReply.Data + 5, // 1: api.InfoReply.data:type_name -> api.InfoReply.Data + 0, // 2: api.BiliAPI.GetUserStat:input_type -> api.StatRequest + 2, // 3: api.BiliAPI.GetUserBasicInfo:input_type -> api.InfoRequest + 1, // 4: api.BiliAPI.GetUserStat:output_type -> api.StatReply + 3, // 5: api.BiliAPI.GetUserBasicInfo:output_type -> api.InfoReply + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_api_proto_init() } +func file_api_proto_init() { + if File_api_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InfoReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatReply_Data); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InfoReply_Data); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_api_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_api_proto_goTypes, + DependencyIndexes: file_api_proto_depIdxs, + MessageInfos: file_api_proto_msgTypes, + }.Build() + File_api_proto = out.File + file_api_proto_rawDesc = nil + file_api_proto_goTypes = nil + file_api_proto_depIdxs = nil +} diff --git a/rpc/pb/api_grpc.pb.go b/rpc/pb/api_grpc.pb.go new file mode 100644 index 0000000..8b4ac8d --- /dev/null +++ b/rpc/pb/api_grpc.pb.go @@ -0,0 +1,141 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.8 +// source: api.proto + +package pb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// BiliAPIClient is the client API for BiliAPI service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BiliAPIClient interface { + GetUserStat(ctx context.Context, in *StatRequest, opts ...grpc.CallOption) (*StatReply, error) + GetUserBasicInfo(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoReply, error) +} + +type biliAPIClient struct { + cc grpc.ClientConnInterface +} + +func NewBiliAPIClient(cc grpc.ClientConnInterface) BiliAPIClient { + return &biliAPIClient{cc} +} + +func (c *biliAPIClient) GetUserStat(ctx context.Context, in *StatRequest, opts ...grpc.CallOption) (*StatReply, error) { + out := new(StatReply) + err := c.cc.Invoke(ctx, "/api.BiliAPI/GetUserStat", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *biliAPIClient) GetUserBasicInfo(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoReply, error) { + out := new(InfoReply) + err := c.cc.Invoke(ctx, "/api.BiliAPI/GetUserBasicInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BiliAPIServer is the server API for BiliAPI service. +// All implementations must embed UnimplementedBiliAPIServer +// for forward compatibility +type BiliAPIServer interface { + GetUserStat(context.Context, *StatRequest) (*StatReply, error) + GetUserBasicInfo(context.Context, *InfoRequest) (*InfoReply, error) + mustEmbedUnimplementedBiliAPIServer() +} + +// UnimplementedBiliAPIServer must be embedded to have forward compatible implementations. +type UnimplementedBiliAPIServer struct { +} + +func (UnimplementedBiliAPIServer) GetUserStat(context.Context, *StatRequest) (*StatReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUserStat not implemented") +} +func (UnimplementedBiliAPIServer) GetUserBasicInfo(context.Context, *InfoRequest) (*InfoReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUserBasicInfo not implemented") +} +func (UnimplementedBiliAPIServer) mustEmbedUnimplementedBiliAPIServer() {} + +// UnsafeBiliAPIServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BiliAPIServer will +// result in compilation errors. +type UnsafeBiliAPIServer interface { + mustEmbedUnimplementedBiliAPIServer() +} + +func RegisterBiliAPIServer(s grpc.ServiceRegistrar, srv BiliAPIServer) { + s.RegisterService(&BiliAPI_ServiceDesc, srv) +} + +func _BiliAPI_GetUserStat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StatRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BiliAPIServer).GetUserStat(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.BiliAPI/GetUserStat", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BiliAPIServer).GetUserStat(ctx, req.(*StatRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BiliAPI_GetUserBasicInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BiliAPIServer).GetUserBasicInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.BiliAPI/GetUserBasicInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BiliAPIServer).GetUserBasicInfo(ctx, req.(*InfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// BiliAPI_ServiceDesc is the grpc.ServiceDesc for BiliAPI service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BiliAPI_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "api.BiliAPI", + HandlerType: (*BiliAPIServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUserStat", + Handler: _BiliAPI_GetUserStat_Handler, + }, + { + MethodName: "GetUserBasicInfo", + Handler: _BiliAPI_GetUserBasicInfo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} diff --git a/rpc/rpc.go b/rpc/rpc.go new file mode 100644 index 0000000..d1732ae --- /dev/null +++ b/rpc/rpc.go @@ -0,0 +1,31 @@ +package rpc + +import ( + "github.com/eigeen/furryboard/spider-scheduler/pkg/conf" + "github.com/eigeen/furryboard/spider-scheduler/pkg/log" + "github.com/eigeen/furryboard/spider-scheduler/rpc/pb" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "strconv" + "sync" +) + +var ( + Conn *grpc.ClientConn + biliAPI pb.BiliAPIClient + once sync.Once +) + +func SpiderCore() pb.BiliAPIClient { + once.Do(InitBiliAPI) + return biliAPI +} + +func InitBiliAPI() { + addr := conf.Conf.SpiderCore.Host + ":" + strconv.Itoa(int(conf.Conf.SpiderCore.Port)) + Conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Logger().Fatalf("连接到RPC服务器时发生错误:%s", err) + } + biliAPI = pb.NewBiliAPIClient(Conn) +}