From 12ffcbef6abff6cf7d7cd5df8db9f34c40a2350c Mon Sep 17 00:00:00 2001 From: yanweidong Date: Thu, 7 May 2026 00:47:09 +0800 Subject: [PATCH] Implement initial project structure and setup --- .gitignore | 34 +++ cmd/main/main.go | 39 ++++ etc/coin.service | 15 ++ etc/coin_dev.yaml | 27 +++ etc/coin_prod.yaml | 27 +++ etc/coin_test.yaml | 27 +++ go.mod | 82 +++++++ go.sum | 239 +++++++++++++++++++ internal/config/config.go | 41 ++++ internal/impl/impl.go | 35 +++ internal/logic/boot.go | 6 + internal/logic/spot_binance.go | 379 +++++++++++++++++++++++++++++++ internal/models/spot_position.go | 47 ++++ scripts/build.sh | 6 + scripts/deploy.sh | 50 ++++ scripts/update.sh | 7 + 16 files changed, 1061 insertions(+) create mode 100644 .gitignore create mode 100644 cmd/main/main.go create mode 100644 etc/coin.service create mode 100644 etc/coin_dev.yaml create mode 100644 etc/coin_prod.yaml create mode 100644 etc/coin_test.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/config/config.go create mode 100644 internal/impl/impl.go create mode 100644 internal/logic/boot.go create mode 100644 internal/logic/spot_binance.go create mode 100644 internal/models/spot_position.go create mode 100644 scripts/build.sh create mode 100644 scripts/deploy.sh create mode 100644 scripts/update.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ac8500 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# ---> Go +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# 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/ +gen/ +.cache/ +cache/ +logs/ +data/ +.idea/ +.vscode/ +.builds/ +markdata/ + + +# Go workspace file +go.work + +output/ \ No newline at end of file diff --git a/cmd/main/main.go b/cmd/main/main.go new file mode 100644 index 0000000..5aadb26 --- /dev/null +++ b/cmd/main/main.go @@ -0,0 +1,39 @@ +/** + * @Author: david.yan(david.yan@qq.com) + * @Date: 2021-11-26 15:25:03 + */ +package main + +import ( + "fmt" + + "git.apinb.com/bsm-sdk/core/middleware" + "git.apinb.com/quant/coin/internal/config" + "git.apinb.com/quant/coin/internal/impl" + "git.apinb.com/quant/coin/internal/logic" + "github.com/gin-gonic/gin" +) + +var ( + ServiceKey = "coin" +) + +func main() { + config.New(ServiceKey) + + impl.NewImpl() + go logic.Boot() + + // 初始化Gin引擎 + app := gin.Default() + + // use middleware + app.Use(middleware.Cors()) + app.Use(gin.Recovery()) + + // 启动HTTP服务器 + err := app.Run(fmt.Sprintf(":%s", config.Spec.Port)) + if err != nil { + panic(err) + } +} diff --git a/etc/coin.service b/etc/coin.service new file mode 100644 index 0000000..9e00252 --- /dev/null +++ b/etc/coin.service @@ -0,0 +1,15 @@ +[Unit] +Description=coin Service + +[Service] +Type=simple +Restart=always +RestartSec=2s +Environment="BSM_RuntimeMode=prod" "BSM_Prefix=/data/app/" +ExecStart=/data/app/coin +WorkingDirectory=/data/app/ +StandardOutput=file:/data/app/logs/coin.log + + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/etc/coin_dev.yaml b/etc/coin_dev.yaml new file mode 100644 index 0000000..b5ca368 --- /dev/null +++ b/etc/coin_dev.yaml @@ -0,0 +1,27 @@ +Service: coin +Port: 16499 +ApiPort: 163602 +WebPort: 16500 + +Databases: + Driver: postgres + Source: + - host=139.224.247.176 user=postgres password=Stock0310~! dbname=coin_prod port=19432 sslmode=disable TimeZone=Asia/Shanghai + +# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。 +Cache: redis://null:Weidong2023~!@139.224.247.176:19379/1 + +# Binance 现货 API(需同时在 Binance 控制台开启现货交易权限) +BinanceApiKey: "" +BinanceApiSecret: "" + +# 日志配置 +Log: + Level: 0 # 0:debug, 1:info, 2:warn, 3:error, 4:fatal + Dir: logs + Endpoint: http://127.0.0.1:13299/log + Remote: false + Console: true + File: true + + diff --git a/etc/coin_prod.yaml b/etc/coin_prod.yaml new file mode 100644 index 0000000..edb7de8 --- /dev/null +++ b/etc/coin_prod.yaml @@ -0,0 +1,27 @@ +Service: coin +Port: 16499 +ApiPort: 163602 +WebPort: 16500 + +Databases: + Driver: postgres + Source: + - host=139.224.247.176 user=postgres password=Stock0310~! dbname=coin_prod port=19432 sslmode=disable TimeZone=Asia/Shanghai + +# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。 +Cache: redis://null:Weidong2023~!@139.224.247.176:19379/1 + +# Binance 现货 API +BinanceApiKey: "" +BinanceApiSecret: "" + +# 日志配置 +Log: + Level: 0 # 0:debug, 1:info, 2:warn, 3:error, 4:fatal + Dir: logs + Endpoint: http://127.0.0.1:13299/log + Remote: false + Console: true + File: true + + diff --git a/etc/coin_test.yaml b/etc/coin_test.yaml new file mode 100644 index 0000000..edb7de8 --- /dev/null +++ b/etc/coin_test.yaml @@ -0,0 +1,27 @@ +Service: coin +Port: 16499 +ApiPort: 163602 +WebPort: 16500 + +Databases: + Driver: postgres + Source: + - host=139.224.247.176 user=postgres password=Stock0310~! dbname=coin_prod port=19432 sslmode=disable TimeZone=Asia/Shanghai + +# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。 +Cache: redis://null:Weidong2023~!@139.224.247.176:19379/1 + +# Binance 现货 API +BinanceApiKey: "" +BinanceApiSecret: "" + +# 日志配置 +Log: + Level: 0 # 0:debug, 1:info, 2:warn, 3:error, 4:fatal + Dir: logs + Endpoint: http://127.0.0.1:13299/log + Remote: false + Console: true + File: true + + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..111d0e2 --- /dev/null +++ b/go.mod @@ -0,0 +1,82 @@ +module git.apinb.com/quant/coin + +go 1.26.1 + +require ( + git.apinb.com/bsm-sdk/core v0.1.9 + git.apinb.com/quant/gostock v0.0.0-20260506155721-e9675449beee + github.com/gin-gonic/gin v1.12.0 + github.com/patrickmn/go-cache v2.1.0+incompatible + gopkg.in/yaml.v3 v3.0.1 + gorm.io/gorm v1.31.1 +) + +require ( + filippo.io/edwards25519 v1.2.0 // indirect + github.com/adshao/go-binance/v2 v2.8.11 // indirect + github.com/allegro/bigcache/v3 v3.1.0 // indirect + github.com/bitly/go-simplejson v0.5.0 // indirect + github.com/bytedance/gopkg v0.1.4 // indirect + github.com/bytedance/sonic v1.15.1 // indirect + github.com/bytedance/sonic/loader v0.5.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.7 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/gin-contrib/cors v1.7.7 // indirect + github.com/gin-contrib/sse v1.1.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.2 // indirect + github.com/go-sql-driver/mysql v1.10.0 // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oklog/ulid/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.3.1 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/redis/go-redis/v9 v9.19.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.etcd.io/etcd/api/v3 v3.6.11 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.11 // indirect + go.etcd.io/etcd/client/v3 v3.6.11 // indirect + go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.28.0 // indirect + golang.org/x/arch v0.26.0 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348 // indirect + google.golang.org/grpc v1.81.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gorm.io/driver/mysql v1.6.0 // indirect + gorm.io/driver/postgres v1.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..72e511b --- /dev/null +++ b/go.sum @@ -0,0 +1,239 @@ +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +git.apinb.com/bsm-sdk/core v0.1.9 h1:Pp0zpSeX7OnFb3JQdXdJHR74EvU02HnMauk4wI0/gVg= +git.apinb.com/bsm-sdk/core v0.1.9/go.mod h1:R5Rm/Ep4D3mJ97dL6sLRi3jtfQxi2ftekp2E3frts/8= +git.apinb.com/quant/gostock v0.0.0-20260506155721-e9675449beee h1:SCF2GT5v5G3IDnRY+HfVy/+cftmdIUmCLFZlN5aRMu8= +git.apinb.com/quant/gostock v0.0.0-20260506155721-e9675449beee/go.mod h1:UqerotKMQ8d3GxA/s0DbFRog+mhMNF0ND0gmxajyxx4= +github.com/adshao/go-binance/v2 v2.8.11 h1:uE9bERWjrUMykankotJrEdSmweYL65Zv/auQHzutEMM= +github.com/adshao/go-binance/v2 v2.8.11/go.mod h1:XkkuecSyJKPolaCGf/q4ovJYB3t0P+7RUYTbGr+LMGM= +github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= +github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM= +github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4= +github.com/bytedance/sonic v1.15.1 h1:nJD5PmM0vY7J8CT6MxoqbVAAMhkSmV2HgRAUrrpLoOw= +github.com/bytedance/sonic v1.15.1/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= +github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI= +github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= +github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= +github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= +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/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/cors v1.7.7 h1:Oh9joP463x7Mw72vhvJ61YQm8ODh9b04YR7vsOErD0Q= +github.com/gin-contrib/cors v1.7.7/go.mod h1:K5tW0RkzJtWSiOdikXloy8VEZlgdVNpHNw8FpjUPNrE= +github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko= +github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ= +github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= +github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw= +github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= +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/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +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.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +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/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= +github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +go.etcd.io/etcd/api/v3 v3.6.11 h1:XFGTgrJ8nak3kB4NgMG8t7NT+lEeuuvKQAqUHKVgkWQ= +go.etcd.io/etcd/api/v3 v3.6.11/go.mod h1:HYfTh0jyh+uFgp6gMbxJteIDYY97yMuYz85Rnw6Gy9o= +go.etcd.io/etcd/client/pkg/v3 v3.6.11 h1:e41mp315Yn3QMGPmEzCyLsMINgJXTY/dX8kM++1csxU= +go.etcd.io/etcd/client/pkg/v3 v3.6.11/go.mod h1:DysuMe/inqRyC/1tjRR6hReH/VV9Lufs27YKSKBWWJg= +go.etcd.io/etcd/client/v3 v3.6.11 h1:LAByD96VmmeuairkvdAcE0RZnrmGz/q3ceeWePo9bwc= +go.etcd.io/etcd/client/v3 v3.6.11/go.mod h1:vOTDMCo+fGPEClJqcFEFSqZ+8e7WKV7AyqJjX//HR2w= +go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8= +go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.26.0 h1:jZ6dpec5haP/fUv1kLCbuJy6dnRrfX6iVK08lZBFpk4= +golang.org/x/arch v0.26.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= +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.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +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/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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/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= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348 h1:U8orV30l6KpDsi9dxU0CoJZGbjS8EEpw+6ba+XwGPQA= +google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348/go.mod h1:Yzdzr5OOZFgSsEV2D/Xi9NL3bszpXFAg0hFJiRohcD8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348 h1:pfIbyB44sWzHiCpRqIen67ZQnVXSfIxWrqUMk1qwODE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= +google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/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/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= +gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..4019bc6 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,41 @@ +package config + +import ( + "net" + + "git.apinb.com/bsm-sdk/core/conf" + "git.apinb.com/bsm-sdk/core/utils" +) + +var ( + Spec SrvConfig +) + +type SrvConfig struct { + conf.Base `yaml:",inline"` + ApiPort string `yaml:"ApiPort"` + WebPort string `yaml:"WebPort"` + Databases *conf.DBConf `yaml:"Databases"` + BinanceApiKey string `yaml:"BinanceApiKey"` + BinanceApiSecret string `yaml:"BinanceApiSecret"` +} + +func New(srvKey string) { + // 初始化配置 创建一个新的配置实例,用于服务配置 + conf.New(srvKey, &Spec) + + // 配置校验 服务IP,端口; 端口如果不合规,则随机分配端口 + Spec.Port = conf.CheckPort(Spec.Port) + Spec.BindIP = conf.CheckIP(Spec.BindIP) + Spec.Addr = net.JoinHostPort(Spec.BindIP, Spec.Port) + + // 配置校验 服务名称地址及监听地址不能为空 + conf.NotNil(Spec.Service, Spec.Cache) + + // check dirs + if !utils.PathExists("./cache") { + utils.CreateDir("./cache") + } + + conf.PrintInfo(Spec.Addr) +} diff --git a/internal/impl/impl.go b/internal/impl/impl.go new file mode 100644 index 0000000..609be43 --- /dev/null +++ b/internal/impl/impl.go @@ -0,0 +1,35 @@ +package impl + +import ( + "time" + + "git.apinb.com/bsm-sdk/core/cache/redis" + "git.apinb.com/bsm-sdk/core/types" + "git.apinb.com/bsm-sdk/core/vars" + "git.apinb.com/bsm-sdk/core/with" + "git.apinb.com/quant/coin/internal/config" + cache "github.com/patrickmn/go-cache" + "gorm.io/gorm" +) + +var ( + RedisService *redis.RedisClient // Redis 客户端服务 + DBService *gorm.DB + ProdDBService *gorm.DB + MemoryService *cache.Cache +) + +func NewImpl() { + // 初始化 Redis 缓存 + RedisService = with.RedisCache(config.Spec.Cache) + MemoryService = cache.New(24*time.Hour, 24*time.Hour) + + // model + DBService = with.Databases(config.Spec.Databases, &types.SqlOptions{ + MaxIdleConns: vars.SqlOptionMaxIdleConns, + MaxOpenConns: vars.SqlOptionMaxOpenConns, + ConnMaxLifetime: vars.SqlOptionConnMaxLifetime, + LogStdout: false, + Debug: false, + }) +} diff --git a/internal/logic/boot.go b/internal/logic/boot.go new file mode 100644 index 0000000..6102d7c --- /dev/null +++ b/internal/logic/boot.go @@ -0,0 +1,6 @@ +package logic + +// Boot 阻塞运行 Binance 现货策略(建仓、下跌加仓、上涨减仓);由 main 中 go 调用。 +func Boot() { + runBinanceSpotStrategy() +} diff --git a/internal/logic/spot_binance.go b/internal/logic/spot_binance.go new file mode 100644 index 0000000..e41359d --- /dev/null +++ b/internal/logic/spot_binance.go @@ -0,0 +1,379 @@ +// Package logic 提供 Binance 现货策略;本文件为定时轮询(非 WebSocket)的建仓、超跌加仓与冲高减仓。 +package logic + +import ( + "context" + "errors" + "log" + "strconv" + "sync" + "time" + + "git.apinb.com/quant/coin/internal/config" + "git.apinb.com/quant/coin/internal/impl" + "git.apinb.com/quant/coin/internal/models" + "github.com/adshao/go-binance/v2" + "github.com/shopspring/decimal" +) + +// 以下为可调参数,改数值即可调整策略行为(改后重新编译运行)。 +const ( + // pollInterval 两次完整轮询之间的间隔(拉余额、价格、判单、写库) + pollInterval = 60 * time.Second + + // buyQuoteUSDT 每次市价买入使用的报价资产数量(USDT) + buyQuoteUSDT = 100.0 + // dipPct 相对成本价的下跌比例,跌破则触发加仓(若本波未锁) + dipPct = 0.10 + // profitPct 相对成本价的上涨比例,涨破则触发减仓(若本波未锁) + profitPct = 0.10 + // sellFraction 上涨触发时,卖出当前可用基础资产的比例 + sellFraction = 0.20 + + // minHoldUSDT 持仓市值低于此值(USDT)视为「无仓」,会先建首笔 100U + minHoldUSDT = 10.0 + // minSellNotional 单笔卖出名义价值下限,低于则不下单(贴近交易所 minNotional) + minSellNotional = 10.0 + + // dipRecoverPct 超跌加仓后,现价需高于「成本×(1+本值)」才解除加仓锁,避免同一低位反复买 + dipRecoverPct = 0.03 + // rallyResetHighPct 冲高减仓后,现价需低于「成本×(1+本值)」才解除减仓锁,避免同一高位反复卖 + rallyResetHighPct = 0.06 +) + +// spotSymbol 把「账户里的资产名」和「交易对」绑在一起,避免硬编码散落各处。 +type spotSymbol struct { + Base string // 如 BTC + Symbol string // 如 BTCUSDT +} + +// watchList 策略监控的标的列表;增删币种时改此处即可。 +var watchList = []spotSymbol{ + {"BTC", "BTCUSDT"}, + {"ETH", "ETHUSDT"}, + {"SOL", "SOLUSDT"}, + {"XRP", "XRPUSDT"}, +} + +var ( + portfolioMu sync.Mutex // 保护 portfolio 与数据库写入,避免并发轮询(若以后拆协程) + portfolio = models.NewSpotPortfolioSnapshot() + // stepSizes 各交易对 LOT_SIZE 的 stepSize,用于卖出数量按交易所步长向下取整 + stepSizes = map[string]string{} +) + +// runBinanceSpotStrategy 入口:校验配置 → 连通性 → 死循环定时执行 spotTick。 +// 若 Key/Secret 为空或 API 失败会直接 return,不再占用协程(由 Boot 调用方决定是否在 goroutine 里跑)。 +func runBinanceSpotStrategy() { + ctx := context.Background() + key := config.Spec.BinanceApiKey + secret := config.Spec.BinanceApiSecret + if key == "" || secret == "" { + log.Printf("logic: 未配置 BinanceApiKey 或 BinanceApiSecret,跳过现货策略") + return + } + client := binance.NewClient(key, secret) + if err := client.NewPingService().Do(ctx); err != nil { + log.Printf("logic: Binance Ping 失败: %v", err) + return + } + acct, err := client.NewGetAccountService().Do(ctx) + if err != nil { + log.Printf("logic: Binance 账户校验失败: %v", err) + return + } + if !acct.CanTrade { + log.Printf("logic: Binance 账户未开启现货交易权限") + return + } + log.Printf("logic: Binance 已连接,CanTrade=%v", acct.CanTrade) + + if err := loadPortfolio(); err != nil { + log.Printf("logic: 从数据库加载现货持仓失败(将使用空档): %v", err) + } + if err := refreshStepSizes(ctx, client); err != nil { + log.Printf("logic: 加载交易对精度失败: %v", err) + } + + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + for { + if err := spotTick(ctx, client); err != nil { + log.Printf("logic: 现货策略轮询错误: %v", err) + } + <-ticker.C + } +} + +// refreshStepSizes 从 exchangeInfo 拉取各 symbol 的 LOT_SIZE.stepSize,供卖出数量格式化。 +func refreshStepSizes(ctx context.Context, client *binance.Client) error { + syms := make([]string, 0, len(watchList)) + for _, w := range watchList { + syms = append(syms, w.Symbol) + } + info, err := client.NewExchangeInfoService().Symbols(syms...).Do(ctx) + if err != nil { + return err + } + for _, s := range info.Symbols { + lot := s.LotSizeFilter() + if lot == nil || lot.StepSize == "" { + continue + } + stepSizes[s.Symbol] = lot.StepSize + } + return nil +} + +// loadPortfolio 从 GORM 读入全表 spot_positions,填充内存 map(键为 BaseAsset)。 +func loadPortfolio() error { + portfolioMu.Lock() + defer portfolioMu.Unlock() + if impl.DBService == nil { + portfolio = models.NewSpotPortfolioSnapshot() + return nil + } + var rows []models.SpotPosition + if err := impl.DBService.Find(&rows).Error; err != nil { + return err + } + portfolio = models.NewSpotPortfolioSnapshot() + for i := range rows { + p := new(models.SpotPosition) + *p = rows[i] + portfolio.Positions[p.BaseAsset] = p + } + return nil +} + +// savePortfolioLocked 将内存中各 SpotPosition 以 Save 写回数据库(有主键则更新,无则插入)。 +// 调用方必须已持有 portfolioMu。 +func savePortfolioLocked() error { + if impl.DBService == nil { + return nil + } + for _, st := range portfolio.Positions { + if st == nil { + continue + } + if err := impl.DBService.Save(st).Error; err != nil { + return err + } + } + return nil +} + +// balanceFree 从账户余额列表里解析某资产的可用数量(Free 字段为字符串)。 +func balanceFree(balances []binance.Balance, asset string) (float64, error) { + for _, b := range balances { + if b.Asset == asset { + return strconv.ParseFloat(b.Free, 64) + } + } + return 0, nil +} + +// spotTick 单次轮询:拉账户 + 批量现价 → 对每个 watchList 标的执行 processOne → 持久化到库。 +func spotTick(ctx context.Context, client *binance.Client) error { + acct, err := client.NewGetAccountService().Do(ctx) + if err != nil { + return err + } + symbols := make([]string, len(watchList)) + for i, w := range watchList { + symbols[i] = w.Symbol + } + prices, err := client.NewListPricesService().Symbols(symbols).Do(ctx) + if err != nil { + return err + } + priceBySymbol := make(map[string]float64, len(prices)) + for _, p := range prices { + v, err := strconv.ParseFloat(p.Price, 64) + if err != nil { + continue + } + priceBySymbol[p.Symbol] = v + } + + portfolioMu.Lock() + defer portfolioMu.Unlock() + + for _, w := range watchList { + px, ok := priceBySymbol[w.Symbol] + if !ok { + continue + } + if err := processOne(ctx, client, acct.Balances, w, px); err != nil { + log.Printf("logic: %s 处理失败: %v", w.Symbol, err) + } + } + return savePortfolioLocked() +} + +// getOrCreateState 按基础资产取状态;不存在则新建并挂到 portfolio 上(首轮 Save 时插入表)。 +func getOrCreateState(base, symbol string) *models.SpotPosition { + st, ok := portfolio.Positions[base] + if !ok { + st = &models.SpotPosition{ + BaseAsset: base, + Symbol: symbol, + } + portfolio.Positions[base] = st + } + return st +} + +// processOne 单标的一轮决策:无仓建仓 → 有仓则判超跌买 / 冲高卖,并维护锁与成本。 +func processOne(ctx context.Context, client *binance.Client, balances []binance.Balance, w spotSymbol, price float64) error { + free, err := balanceFree(balances, w.Base) + if err != nil { + return err + } + st := getOrCreateState(w.Base, w.Symbol) + positionUSDT := free * price + + // ---------- 分支一:视为空仓,首笔市价买 100 USDT ---------- + if positionUSDT < minHoldUSDT { + st.DipLegLocked = false + st.RallyLegLocked = false + usdtFree, err := balanceFree(balances, "USDT") + if err != nil { + return err + } + if usdtFree < buyQuoteUSDT { + return errors.New("USDT 余额不足,无法建仓 100U") + } + order, err := client.NewCreateOrderService(). + Symbol(w.Symbol). + Side(binance.SideTypeBuy). + Type(binance.OrderTypeMarket). + QuoteOrderQty(strconv.FormatFloat(buyQuoteUSDT, 'f', 2, 64)). + NewOrderRespType(binance.NewOrderRespTypeFULL). + Do(ctx) + if err != nil { + return err + } + applyBuyFill(st, order) + log.Printf("logic: %s 初始建仓约 %.2f USDT, 成交均价约 %.4f", w.Symbol, buyQuoteUSDT, st.AvgCostUSDT) + return nil + } + + // 有仓但本地从未写过成本(例如手工转入),用当前价初始化成本,便于后续比例判断 + if st.AvgCostUSDT <= 0 { + st.AvgCostUSDT = price + } + st.Quantity = free + + // ---------- 分支二:相对成本下跌 dipPct,且本波未加仓过 → 再买 100 USDT ---------- + dipLine := st.AvgCostUSDT * (1 - dipPct) + if price <= dipLine && !st.DipLegLocked { + usdtFree, err := balanceFree(balances, "USDT") + if err != nil { + return err + } + if usdtFree < buyQuoteUSDT { + return errors.New("USDT 余额不足,无法加仓 100U") + } + order, err := client.NewCreateOrderService(). + Symbol(w.Symbol). + Side(binance.SideTypeBuy). + Type(binance.OrderTypeMarket). + QuoteOrderQty(strconv.FormatFloat(buyQuoteUSDT, 'f', 2, 64)). + NewOrderRespType(binance.NewOrderRespTypeFULL). + Do(ctx) + if err != nil { + return err + } + applyBuyFill(st, order) + st.DipLegLocked = true + log.Printf("logic: %s 下跌 %.0f%% 加仓 %.0f USDT, 新成本约 %.4f", w.Symbol, dipPct*100, buyQuoteUSDT, st.AvgCostUSDT) + } + // 价格明显站回成本上方后,允许下一次「超跌加仓」 + if st.DipLegLocked && price >= st.AvgCostUSDT*(1+dipRecoverPct) { + st.DipLegLocked = false + } + + // ---------- 分支三:相对成本上涨 profitPct,且本波未减仓过 → 卖出 free 的 sellFraction ---------- + profitLine := st.AvgCostUSDT * (1 + profitPct) + if price >= profitLine && !st.RallyLegLocked { + step := stepSizes[w.Symbol] + if step == "" { + step = "0.00001" + } + qtyStr, ok, err := formatQtyForSell(free*sellFraction, step) + if err != nil { + return err + } + if !ok || parseFloat(qtyStr)*price < minSellNotional { + return nil + } + order, err := client.NewCreateOrderService(). + Symbol(w.Symbol). + Side(binance.SideTypeSell). + Type(binance.OrderTypeMarket). + Quantity(qtyStr). + NewOrderRespType(binance.NewOrderRespTypeFULL). + Do(ctx) + if err != nil { + return err + } + sold, _ := strconv.ParseFloat(order.ExecutedQuantity, 64) + st.Quantity = free - sold + if st.Quantity < 0 { + st.Quantity = 0 + } + st.RallyLegLocked = true + log.Printf("logic: %s 上涨 %.0f%% 减持 %.0f%%, 卖出数量 %s", w.Symbol, profitPct*100, sellFraction*100, qtyStr) + } + // 从高位回落靠近成本后,允许下一次「冲高减仓」 + if st.RallyLegLocked && price <= st.AvgCostUSDT*(1+rallyResetHighPct) { + st.RallyLegLocked = false + } + + return nil +} + +// applyBuyFill 根据市价买单成交回报更新加权成本与数量(首笔建仓与加仓共用)。 +func applyBuyFill(st *models.SpotPosition, order *binance.CreateOrderResponse) { + execQty, _ := strconv.ParseFloat(order.ExecutedQuantity, 64) + quote, _ := strconv.ParseFloat(order.CummulativeQuoteQuantity, 64) + if execQty <= 0 || quote <= 0 { + return + } + oldQty := st.Quantity + oldCost := st.AvgCostUSDT + newQty := oldQty + execQty + if newQty <= 0 { + return + } + if oldQty <= 0 || oldCost <= 0 { + st.AvgCostUSDT = quote / execQty + } else { + st.AvgCostUSDT = (oldQty*oldCost + quote) / newQty + } + st.Quantity = newQty +} + +// formatQtyForSell 将「计划卖出数量」按 stepSize 向下取整,满足 Binance LOT_SIZE;过小则返回 ok=false。 +func formatQtyForSell(qty float64, stepSize string) (string, bool, error) { + if qty <= 0 { + return "", false, nil + } + step, err := decimal.NewFromString(stepSize) + if err != nil { + return "", false, err + } + q := decimal.NewFromFloat(qty) + n := q.Div(step).Floor() + out := n.Mul(step) + if out.LessThanOrEqual(decimal.Zero) { + return "", false, nil + } + return out.String(), true, nil +} + +func parseFloat(s string) float64 { + v, _ := strconv.ParseFloat(s, 64) + return v +} diff --git a/internal/models/spot_position.go b/internal/models/spot_position.go new file mode 100644 index 0000000..dff7a65 --- /dev/null +++ b/internal/models/spot_position.go @@ -0,0 +1,47 @@ +// Package models 中的现货持仓表模型,由 logic 包读写;表名 spot_positions。 +package models + +import ( + "time" + + "git.apinb.com/bsm-sdk/core/database" +) + +// SpotPosition 现货策略在库中的一行:按 BaseAsset 唯一,对应 watchList 里每个基础资产。 +type SpotPosition struct { + ID uint `gorm:"primarykey"` + CreatedAt time.Time + UpdatedAt time.Time + + // BaseAsset 基础资产代码,如 BTC;唯一索引,用于 upsert 与内存 map 的键 + BaseAsset string `gorm:"uniqueIndex:uk_spot_base_asset;size:32;not null"` + // Symbol 交易对,如 BTCUSDT + Symbol string `gorm:"size:32;not null"` + // AvgCostUSDT 加权平均持仓成本(USDT/枚) + AvgCostUSDT float64 `gorm:"column:avg_cost_usdt;not null;default:0"` + // Quantity 策略侧同步的持仓数量(枚) + Quantity float64 `gorm:"not null;default:0"` + // DipLegLocked 超跌加仓波段锁 + DipLegLocked bool `gorm:"column:dip_leg_locked;not null;default:false"` + // RallyLegLocked 冲高减仓波段锁 + RallyLegLocked bool `gorm:"column:rally_leg_locked;not null;default:false"` +} + +func init() { + database.AppendMigrate(&SpotPosition{}) +} + +// TableName 显式表名,避免与结构体名推导不一致。 +func (SpotPosition) TableName() string { + return "spot_positions" +} + +// SpotPortfolioSnapshot 内存中的组合视图:从数据库加载后填充 Positions。 +type SpotPortfolioSnapshot struct { + Positions map[string]*SpotPosition +} + +// NewSpotPortfolioSnapshot 创建空快照。 +func NewSpotPortfolioSnapshot() *SpotPortfolioSnapshot { + return &SpotPortfolioSnapshot{Positions: make(map[string]*SpotPosition)} +} diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..2043caf --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +GOARCH=amd64 GOOS=linux go build -o ../builds/coin ./cmd/main/main.go + +BSM_RuntimeMode=prod BSM_Prefix=/data/app/ nohup ./coin > /data/app/logs/coin.log 2>&1 & +cat /data/app/logs/coin.log diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 0000000..36d498a --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# 配置部分 +BINARY_NAME="gostock" # 二进制文件名 +BUILD_OUTPUT_DIR="../builds/gostock" # 构建输出目录 + +# 服务器配置 +REMOTE_USER="root" # 服务器用户名 +REMOTE_HOST="139.224.247.176" # 服务器地址 +REMOTE_DIR="/data/app" # 服务器部署目录 +SERVICE_NAME="gostock" # 服务名称(如果有systemd服务) + +echo "=== 开始部署流程 ===" + +# 1. 编译Linux二进制文件 +echo "正在编译Linux二进制文件..." + +# 使用Go语言编译示例 (如果是其他语言请修改此部分) +# 如果不是Go项目,请替换为你的构建命令,如make等 +GOEXPERIMENT=jsonv2 GOOS=linux GOARCH=amd64 go build -o "${BUILD_OUTPUT_DIR}/${BINARY_NAME}" ./cmd/${BINARY_NAME}/main.go + +if [ $? -ne 0 ]; then + echo "编译失败!" + exit 1 +fi + +echo "编译成功: ${BUILD_OUTPUT_DIR}/${BINARY_NAME}" + +# 2. 停止远程服务 +echo "正在停止远程服务..." +ssh "${REMOTE_USER}@${REMOTE_HOST}" << EOF + killall -9 "${BINARY_NAME}" +EOF + +# 3. 上传到服务器 +echo "正在上传文件到服务器..." +scp -C "${BUILD_OUTPUT_DIR}/${BINARY_NAME}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/${BINARY_NAME}" +scp ./etc/* "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/etc/" + + +# 4. 设置执行权限并启动服务 +echo "正在设置权限并启动服务..." +ssh "${REMOTE_USER}@${REMOTE_HOST}" << EOF + chmod +x "${REMOTE_DIR}/${BINARY_NAME}" + nohup "${REMOTE_DIR}/${BINARY_NAME}" > "${REMOTE_DIR}/logs/${BINARY_NAME}.log" 2>&1 & + sleep 2 + pgrep -f "${REMOTE_DIR}/${BINARY_NAME}" && echo "服务启动成功!" || echo "服务启动可能失败!" +EOF + +echo "=== 部署完成 ===" \ No newline at end of file diff --git a/scripts/update.sh b/scripts/update.sh new file mode 100644 index 0000000..f6ec2de --- /dev/null +++ b/scripts/update.sh @@ -0,0 +1,7 @@ +git pull +go get all +go get -u ./... +go mod tidy +git add . +git commit -m 'run ./script/update.sh' +git push \ No newline at end of file