diff --git a/code/allapp/allapp-go-v3/cmd/app/main.go b/code/allapp/allapp-go-v3/cmd/app/main.go index 15150982..97a0e3ad 100644 --- a/code/allapp/allapp-go-v3/cmd/app/main.go +++ b/code/allapp/allapp-go-v3/cmd/app/main.go @@ -5,5 +5,14 @@ import ( ) func main() { - _ = bootstrap.BootService() + app, err := bootstrap.NewApp() + if err != nil { + panic(err) + } + + defer app.Close() + + if err := app.Run(); err != nil { + panic(err) + } } diff --git a/code/allapp/allapp-go-v3/configs/config.dev.yaml b/code/allapp/allapp-go-v3/configs/config.dev.yaml index 9b3af9a1..5a3b584a 100644 --- a/code/allapp/allapp-go-v3/configs/config.dev.yaml +++ b/code/allapp/allapp-go-v3/configs/config.dev.yaml @@ -1,2 +1,13 @@ app: port: 8087 +postgres: + host: 117.72.182.135 + port: 5432 + user: postgres + password: zhang520.. + dbname: app + timezone: Asia/Shanghai + max_open_conns: 10 + max_idle_conns: 5 + conn_max_lifetime: 30m + sll_mode: false \ No newline at end of file diff --git a/code/allapp/allapp-go-v3/go.mod b/code/allapp/allapp-go-v3/go.mod index 3088e817..c3eef7b9 100644 --- a/code/allapp/allapp-go-v3/go.mod +++ b/code/allapp/allapp-go-v3/go.mod @@ -2,17 +2,23 @@ module allapp-go go 1.25.0 +require ( + github.com/gofiber/fiber/v3 v3.1.0 + github.com/golang-jwt/jwt/v5 v5.3.1 + github.com/jackc/pgx/v5 v5.9.1 + github.com/spf13/viper v1.21.0 +) + require ( github.com/andybalholm/brotli v1.2.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/gofiber/fiber/v3 v3.1.0 // indirect github.com/gofiber/schema v1.7.0 // indirect github.com/gofiber/utils/v2 v2.0.2 // indirect github.com/google/uuid v1.6.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.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/klauspost/compress v1.18.4 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -23,14 +29,17 @@ require ( github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tinylib/msgp v1.6.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.69.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/net v0.50.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect ) diff --git a/code/allapp/allapp-go-v3/go.sum b/code/allapp/allapp-go-v3/go.sum index b9a45098..be89782e 100644 --- a/code/allapp/allapp-go-v3/go.sum +++ b/code/allapp/allapp-go-v3/go.sum @@ -1,8 +1,14 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= 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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gofiber/fiber/v3 v3.1.0 h1:1p4I820pIa+FGxfwWuQZ5rAyX0WlGZbGT6Hnuxt6hKY= @@ -11,6 +17,10 @@ github.com/gofiber/schema v1.7.0 h1:yNM+FNRZjyYEli9Ey0AXRBrAY9jTnb+kmGs3lJGPvKg= github.com/gofiber/schema v1.7.0/go.mod h1:A/X5Ffyru4p9eBdp99qu+nzviHzQiZ7odLT+TwxWhbk= github.com/gofiber/utils/v2 v2.0.2 h1:ShRRssz0F3AhTlAQcuEj54OEDtWF7+HJDwEi/aa6QLI= github.com/gofiber/utils/v2 v2.0.2/go.mod h1:+9Ub4NqQ+IaJoTliq5LfdmOJAA/Hzwf4pXOxOa3RrJ0= +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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -19,8 +29,14 @@ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7Ulw github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= github.com/jackc/pgx/v5 v5.9.1/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/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +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/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -29,9 +45,14 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0 github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/shamaton/msgpack/v3 v3.1.0 h1:jsk0vEAqVvvS9+fTZ5/EcQ9tz860c9pWxJ4Iwecz8gU= +github.com/shamaton/msgpack/v3 v3.1.0/go.mod h1:DcQG8jrdrQCIxr3HlMYkiXdMhK+KfN2CitkyzsQV4uc= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -45,6 +66,8 @@ github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjb github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= @@ -53,20 +76,32 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI= github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 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/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= 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= diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/app.go b/code/allapp/allapp-go-v3/internal/bootstrap/app.go new file mode 100644 index 00000000..8fb98979 --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/bootstrap/app.go @@ -0,0 +1,74 @@ +package bootstrap + +import ( + "allapp-go/internal/bootstrap/boots" + "allapp-go/internal/config" + "allapp-go/pkg/logger" + "os" + "os/signal" + "syscall" + + "github.com/jackc/pgx/v5/pgxpool" +) + +type App struct { + Config *config.Config + PgSql *pgxpool.Pool +} + +// NewApp 初始化(不阻塞) +func NewApp() (*App, error) { + cfg, err := config.Load() + if err != nil { + return nil, err + } + + // 日志 + boots.InitLogger(cfg) + + pgsql, err := boots.InitPgsql(cfg) + if err != nil { + return nil, err + } + + // 其他组件 + boots.InitUniqueId(cfg) + boots.InitJwt(cfg) + + return &App{ + Config: cfg, + PgSql: pgsql, + }, nil +} + +// Run 启动服务(阻塞) +func (a *App) Run() error { + app, err := boots.RunServer(a.Config) + if err != nil { + return err + } + + // 监听退出信号 + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + + <-quit + + logger.Log.Info("shutting down server...") + + if err := app.Shutdown(); err != nil { + logger.Log.Error("shutdown failed") + return err + } + + return nil +} + +// Close 释放资源 +func (a *App) Close() { + if a.PgSql != nil { + a.PgSql.Close() + } + + logger.Sync() +} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/boots/config.go b/code/allapp/allapp-go-v3/internal/bootstrap/boots/config.go new file mode 100644 index 00000000..b30d446c --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/bootstrap/boots/config.go @@ -0,0 +1,18 @@ +package boots + +import ( + "allapp-go/internal/config" + "allapp-go/pkg/logger" +) + +func InitLogger(cfg *config.Config) { + logger.Init(logger.Config{ + Dir: "logs", + Level: "info", + MaxSize: 100, // MB + MaxBackups: 10, + MaxAge: 7, // 天 + Compress: true, + }) + defer logger.Sync() +} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/boots/jwt.go b/code/allapp/allapp-go-v3/internal/bootstrap/boots/jwt.go new file mode 100644 index 00000000..4a231c4b --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/bootstrap/boots/jwt.go @@ -0,0 +1,11 @@ +package boots + +import ( + "allapp-go/internal/config" + "allapp-go/pkg/jwtx" +) + +// InitJwt 初始化Jwt +func InitJwt(cfg *config.Config) { + jwtx.Init(cfg.JWT.Secret, cfg.JWT.AccessExpiry) +} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/boots/pgsql.go b/code/allapp/allapp-go-v3/internal/bootstrap/boots/pgsql.go new file mode 100644 index 00000000..3b6a11ce --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/bootstrap/boots/pgsql.go @@ -0,0 +1,84 @@ +package boots + +import ( + "allapp-go/internal/config" + "context" + "fmt" + "net/url" + "time" + + "github.com/jackc/pgx/v5/pgxpool" +) + +func InitPgsql(cfg *config.Config) (*pgxpool.Pool, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + pg := cfg.Postgres + + // ssl + sslMode := "disable" + if pg.SslMode { + sslMode = "require" + } + + // 密码转义 + password := url.QueryEscape(pg.Password) + + // DSN + dsn := fmt.Sprintf( + "postgres://%s:%s@%s:%d/%s?sslmode=%s", + pg.User, + password, + pg.Host, + pg.Port, + pg.Dbname, + sslMode, + ) + + // 解析配置 + conf, err := pgxpool.ParseConfig(dsn) + if err != nil { + return nil, err + } + + // ====================== + // 连接池配置 + // ====================== + + // 最大连接数 + if pg.MaxOpenConns > 0 { + conf.MaxConns = int32(pg.MaxOpenConns) + } + + // 最小连接数(近似 idle) + if pg.MaxIdleConns > 0 { + conf.MinConns = int32(pg.MaxIdleConns) + } + + // 连接最大生命周期 + if pg.ConnMaxLifetime > 0 { + conf.MaxConnLifetime = pg.ConnMaxLifetime + } + + // ====================== + // 时区设置(正确方式 ⭐) + // ====================== + if pg.TimeZone != "" { + conf.ConnConfig.RuntimeParams["TimeZone"] = pg.TimeZone + } + + // 创建连接池 + db, err := pgxpool.NewWithConfig(ctx, conf) + if err != nil { + return nil, err + } + + // 测试连接 + if err := db.Ping(ctx); err != nil { + db.Close() + return nil, err + } + + return db, nil +} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/boots/server.go b/code/allapp/allapp-go-v3/internal/bootstrap/boots/server.go new file mode 100644 index 00000000..c0789515 --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/bootstrap/boots/server.go @@ -0,0 +1,32 @@ +package boots + +import ( + "allapp-go/internal/config" + "allapp-go/internal/router" + "fmt" + + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/requestid" +) + +func RunServer(cfg *config.Config) (*fiber.App, error) { + app := fiber.New() + + // ===== middleware ===== + app.Use(requestid.New()) + + // TODO: 你可以加 logger middleware / recover + + // ===== router ===== + router.Setup(app) + + addr := fmt.Sprintf(":%d", cfg.App.Port) + + go func() { + if err := app.Listen(addr); err != nil { + panic(err) + } + }() + + return app, nil +} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/boots/uniqueid.go b/code/allapp/allapp-go-v3/internal/bootstrap/boots/uniqueid.go new file mode 100644 index 00000000..336d8831 --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/bootstrap/boots/uniqueid.go @@ -0,0 +1,12 @@ +package boots + +import ( + "allapp-go/internal/config" + "allapp-go/pkg/uniqueid" +) + +// InitUniqueId 初始化雪花id +func InitUniqueId(cfg *config.Config) { + options := uniqueid.NewIdGeneratorOptions(cfg.UniqueID.WorkerID) + uniqueid.SetIdGenerator(options) +} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/bootstrap.go b/code/allapp/allapp-go-v3/internal/bootstrap/bootstrap.go deleted file mode 100644 index 4a152984..00000000 --- a/code/allapp/allapp-go-v3/internal/bootstrap/bootstrap.go +++ /dev/null @@ -1,24 +0,0 @@ -package bootstrap - -import ( - "allapp-go/internal/config" - "log" -) - -type App struct { - Config *config.Config -} - -func BootService() *App { - // 1️⃣ 加载配置 - cfg, err := config.Load() - if err != nil { - log.Fatalf("❌ load config failed: %v", err) - } - - RunServer(cfg) - - return &App{ - Config: cfg, - } -} diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/server.go b/code/allapp/allapp-go-v3/internal/bootstrap/server.go deleted file mode 100644 index 5c82832f..00000000 --- a/code/allapp/allapp-go-v3/internal/bootstrap/server.go +++ /dev/null @@ -1,24 +0,0 @@ -package bootstrap - -import ( - "allapp-go/internal/config" - "allapp-go/internal/router" - "fmt" - "log" - - "github.com/gofiber/fiber/v3" -) - -func RunServer(cfg *config.Config) { - app := fiber.New() - - router.Setup(app) - - addr := fmt.Sprintf(":%d", cfg.App.Port) - - log.Printf("🚀 Server running on %s", addr) - - if err := app.Listen(addr); err != nil { - log.Fatalf("❌ start server failed: %v", err) - } -} diff --git a/code/allapp/allapp-go-v3/internal/config/config.go b/code/allapp/allapp-go-v3/internal/config/config.go index e61e91f2..9f1adb99 100644 --- a/code/allapp/allapp-go-v3/internal/config/config.go +++ b/code/allapp/allapp-go-v3/internal/config/config.go @@ -1,7 +1,11 @@ package config +import "time" + type Config struct { App AppConfig `mapstructure:"app"` + JWT JwtConfig `mapstructure:"jwt"` + UniqueID UniqueIDConfig `mapstructure:"unique_id"` Postgres PostgresConfig `mapstructure:"postgres"` } @@ -11,11 +15,24 @@ type AppConfig struct { } type PostgresConfig struct { - Host string `mapstructure:"host"` - Port int `mapstructure:"port"` - User string `mapstructure:"user"` - Password string `mapstructure:"password"` - Dbname string `mapstructure:"dbname"` - SslMode bool `mapstructure:"ssl_mode"` - TimeZone string `mapstructure:"timezone"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + User string `mapstructure:"user"` + Password string `mapstructure:"password"` + Dbname string `mapstructure:"dbname"` + SslMode bool `mapstructure:"ssl_mode"` + TimeZone string `mapstructure:"timezone"` + MaxOpenConns int32 `mapstructure:"max_open_conns"` + MaxIdleConns int32 `mapstructure:"max_idle_conns"` + ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"` +} + +type JwtConfig struct { + Secret string `mapstructure:"secret"` + AccessExpiry time.Duration `mapstructure:"access_expiry"` +} + +type UniqueIDConfig struct { + DataCenterID uint16 `mapstructure:"datacenter_id"` + WorkerID uint16 `mapstructure:"worker_id"` } diff --git a/code/allapp/allapp-go-v3/internal/config/loader.go b/code/allapp/allapp-go-v3/internal/config/loader.go index 29035d62..c1bb3d04 100644 --- a/code/allapp/allapp-go-v3/internal/config/loader.go +++ b/code/allapp/allapp-go-v3/internal/config/loader.go @@ -14,12 +14,19 @@ func Load() (*Config, error) { v := viper.New() v.SetConfigType("yaml") + // ✅ 默认值 + setDefaults(v) + + // ✅ 支持环境变量(如 APP_PORT → app.port) + v.AutomaticEnv() + v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + // 基础配置 v.AddConfigPath("configs") v.SetConfigName("config") if err := v.ReadInConfig(); err != nil { - return nil, fmt.Errorf("read base config failed: %w", err) + return nil, fmt.Errorf("读取基础配置失败: %w", err) } // 环境配置(覆盖) @@ -27,20 +34,30 @@ func Load() (*Config, error) { v.SetConfigName(envConfigName) if err := v.MergeInConfig(); err != nil { - // dev 允许没有文件,prod 建议必须存在 + // dev 可以没有,prod 必须有 if env == "prod" { - return nil, fmt.Errorf("read env config failed: %w", err) + return nil, fmt.Errorf("读取环境配置失败(%s): %w", env, err) } } var cfg Config if err := v.Unmarshal(&cfg); err != nil { - return nil, fmt.Errorf("unmarshal config failed: %w", err) + return nil, fmt.Errorf("解析配置失败: %w", err) } return &cfg, nil } +// ✅ 默认值统一在这里管理 +func setDefaults(v *viper.Viper) { + v.SetDefault("app.name", "allapp-go") + v.SetDefault("app.port", 8080) + + // 可以继续扩展 + v.SetDefault("unique_id.datacenter_id", 1) + v.SetDefault("unique_id.worker_id", 1) +} + func getEnv(key, defaultVal string) string { val := os.Getenv(key) if val == "" { diff --git a/code/allapp/allapp-go-v3/logs/info/info.log b/code/allapp/allapp-go-v3/logs/info/info.log new file mode 100644 index 00000000..c1e31c30 --- /dev/null +++ b/code/allapp/allapp-go-v3/logs/info/info.log @@ -0,0 +1 @@ +{"level":"INFO","time":"2026-04-02T22:07:05.693+0800","caller":"app/main.go:15","msg":"shutting down server..."} diff --git a/code/allapp/allapp-go-v3/pkg/jwtx/jwtx.go b/code/allapp/allapp-go-v3/pkg/jwtx/jwtx.go new file mode 100644 index 00000000..83c1d46f --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/jwtx/jwtx.go @@ -0,0 +1,112 @@ +package jwtx + +import ( + "context" + "sync" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +// ================== +// 内部管理器 +// ================== + +type jwtManager struct { + secret string + tokenExpiry time.Duration +} + +var ( + manager *jwtManager + once sync.Once +) + +// ================== +// 初始化 +// ================== + +// Init 初始化(只执行一次) +// expiry = -1 表示不过期 +func Init(secret string, expiry time.Duration) { + once.Do(func() { + manager = &jwtManager{ + secret: secret, + tokenExpiry: expiry, + } + }) +} + +func getManager() *jwtManager { + if manager == nil { + panic("jwt未初始化,请先调用Init") + } + return manager +} + +// ================== +// Claims(通用结构) +// ================== + +type CustomClaims struct { + Data map[string]any `json:"data"` + jwt.RegisteredClaims +} + +// ================== +// 对外方法 +// ================== + +// CreateToken 生成 JWT +func CreateToken(ctx context.Context, data map[string]any) (string, error) { + m := getManager() + return m.createToken(data) +} + +// VerifyToken 校验 token 并返回 claims +func VerifyToken(ctx context.Context, tokenString string) (*CustomClaims, bool) { + if tokenString == "" { + return nil, false + } + + m := getManager() + + token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(m.secret), nil + }) + + if err != nil || !token.Valid { + return nil, false + } + + claims, ok := token.Claims.(*CustomClaims) + if !ok { + return nil, false + } + + return claims, true +} + +// ================== +// 内部实现 +// ================== + +func (j *jwtManager) createToken(data map[string]any) (string, error) { + now := time.Now() + + claims := &CustomClaims{ + Data: data, + RegisteredClaims: jwt.RegisteredClaims{ + IssuedAt: jwt.NewNumericDate(now), + }, + } + + // ✅ 只有 >0 才设置过期时间 + // -1 / 0 都表示不过期 + if j.tokenExpiry > 0 { + claims.ExpiresAt = jwt.NewNumericDate(now.Add(j.tokenExpiry)) + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString([]byte(j.secret)) +} diff --git a/code/allapp/allapp-go-v3/pkg/logger/logger.go b/code/allapp/allapp-go-v3/pkg/logger/logger.go new file mode 100644 index 00000000..7daebd8e --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/logger/logger.go @@ -0,0 +1,170 @@ +package logger + +import ( + "context" + "os" + "path/filepath" + "strings" + + "github.com/gofiber/fiber/v3" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" +) + +var Log *zap.Logger + +const TraceIDKey = "request_id" + +// ====================== +// 配置结构 +// ====================== + +type Config struct { + Dir string // 日志目录 + Level string // debug info warn error + MaxSize int // MB + MaxBackups int + MaxAge int // 天 + Compress bool // 是否压缩 +} + +// ====================== +// 初始化 +// ====================== + +func Init(cfg Config) { + if cfg.Dir == "" { + cfg.Dir = "logs" + } + + if cfg.Level == "" { + cfg.Level = "info" + } + + level := parseLevel(cfg.Level) + + encoderConfig := zapcore.EncoderConfig{ + TimeKey: "time", + LevelKey: "level", + MessageKey: "msg", + CallerKey: "caller", + EncodeLevel: zapcore.CapitalLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } + + encoder := zapcore.NewJSONEncoder(encoderConfig) + + // 控制台 + consoleWriter := zapcore.AddSync(os.Stdout) + + // 文件 writer(info) + infoWriter := getWriter(cfg, "info") + + // 文件 writer(error) + errorWriter := getWriter(cfg, "error") + + // info core + infoCore := zapcore.NewCore( + encoder, + zapcore.NewMultiWriteSyncer(consoleWriter, infoWriter), + zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl < zapcore.ErrorLevel && lvl >= level + }), + ) + + // error core + errorCore := zapcore.NewCore( + encoder, + zapcore.NewMultiWriteSyncer(consoleWriter, errorWriter), + zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= zapcore.ErrorLevel + }), + ) + + core := zapcore.NewTee(infoCore, errorCore) + + Log = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) +} + +// ====================== +// lumberjack writer +// ====================== + +func getWriter(cfg Config, level string) zapcore.WriteSyncer { + dir := filepath.Join(cfg.Dir, level) + _ = os.MkdirAll(dir, os.ModePerm) + + return zapcore.AddSync(&lumberjack.Logger{ + Filename: filepath.Join(dir, level+".log"), + MaxSize: cfg.MaxSize, + MaxBackups: cfg.MaxBackups, + MaxAge: cfg.MaxAge, + Compress: cfg.Compress, + }) +} + +// ====================== +// level 解析 +// ====================== + +func parseLevel(lvl string) zapcore.Level { + switch strings.ToLower(lvl) { + case "debug": + return zap.DebugLevel + case "warn": + return zap.WarnLevel + case "error": + return zap.ErrorLevel + default: + return zap.InfoLevel + } +} + +// ====================== +// trace 支持(核心) +// ====================== + +// Fiber 中使用 +func Ctx(c fiber.Ctx) *zap.Logger { + if c == nil { + return Log + } + + traceID, _ := c.Locals(TraceIDKey).(string) + if traceID == "" { + return Log + } + + return Log.With(zap.String("trace_id", traceID)) +} + +// Service / Repo 使用 +func FromCtx(ctx context.Context) *zap.Logger { + if ctx == nil { + return Log + } + + traceID, _ := ctx.Value(TraceIDKey).(string) + if traceID == "" { + return Log + } + + return Log.With(zap.String("trace_id", traceID)) +} + +// 注入 trace +func WithTrace(ctx context.Context, traceID string) context.Context { + return context.WithValue(ctx, TraceIDKey, traceID) +} + +// ====================== +// 优雅退出 +// ====================== + +func Sync() { + if Log != nil { + _ = Log.Sync() + } +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/DefaultIdGenerator.go b/code/allapp/allapp-go-v3/pkg/uniqueid/DefaultIdGenerator.go new file mode 100644 index 00000000..3d2d57f4 --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/DefaultIdGenerator.go @@ -0,0 +1,83 @@ +package uniqueid + +import ( + "strconv" + "time" +) + +type DefaultIdGenerator struct { + Options *IdGeneratorOptions + SnowWorker ISnowWorker + IdGeneratorException IdGeneratorException +} + +func NewDefaultIdGenerator(options *IdGeneratorOptions) *DefaultIdGenerator { + if options == nil { + panic("dig.Options error.") + } + + // 1.BaseTime + minTime := int64(631123200000) // time.Now().AddDate(-30, 0, 0).UnixNano() / 1e6 + if options.BaseTime < minTime || options.BaseTime > time.Now().UnixNano()/1e6 { + panic("BaseTime error.") + } + + // 2.WorkerIdBitLength + if options.WorkerIdBitLength <= 0 { + panic("WorkerIdBitLength error.(range:[1, 21])") + } + if options.WorkerIdBitLength+options.SeqBitLength > 22 { + panic("error:WorkerIdBitLength + SeqBitLength <= 22") + } + + // 3.WorkerId + maxWorkerIdNumber := uint16(1< maxWorkerIdNumber { + panic("WorkerId error. (range:[0, " + strconv.FormatUint(uint64(maxWorkerIdNumber), 10) + "]") + } + + // 4.SeqBitLength + if options.SeqBitLength < 2 || options.SeqBitLength > 21 { + panic("SeqBitLength error. (range:[2, 21])") + } + + // 5.MaxSeqNumber + maxSeqNumber := uint32(1< maxSeqNumber { + panic("MaxSeqNumber error. (range:[1, " + strconv.FormatUint(uint64(maxSeqNumber), 10) + "]") + } + + // 6.MinSeqNumber + if options.MinSeqNumber < 5 || options.MinSeqNumber > maxSeqNumber { + panic("MinSeqNumber error. (range:[5, " + strconv.FormatUint(uint64(maxSeqNumber), 10) + "]") + } + + var snowWorker ISnowWorker + switch options.Method { + case 1: + snowWorker = NewSnowWorkerM1(options) + case 2: + snowWorker = NewSnowWorkerM2(options) + default: + snowWorker = NewSnowWorkerM1(options) + } + + if options.Method == 1 { + time.Sleep(time.Duration(500) * time.Microsecond) + } + + return &DefaultIdGenerator{ + Options: options, + SnowWorker: snowWorker, + } +} + +func (dig DefaultIdGenerator) NewLong() int64 { + return dig.SnowWorker.NextId() +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/IIdGenerator.go b/code/allapp/allapp-go-v3/pkg/uniqueid/IIdGenerator.go new file mode 100644 index 00000000..a0c1e4de --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/IIdGenerator.go @@ -0,0 +1,5 @@ +package uniqueid + +type IIdGenerator interface { + NewLong() uint64 +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/ISnowWorker.go b/code/allapp/allapp-go-v3/pkg/uniqueid/ISnowWorker.go new file mode 100644 index 00000000..c066e6d6 --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/ISnowWorker.go @@ -0,0 +1,5 @@ +package uniqueid + +type ISnowWorker interface { + NextId() int64 +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/IdGeneratorException.go b/code/allapp/allapp-go-v3/pkg/uniqueid/IdGeneratorException.go new file mode 100644 index 00000000..c0ae9086 --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/IdGeneratorException.go @@ -0,0 +1,12 @@ +package uniqueid + +import "fmt" + +type IdGeneratorException struct { + message string + error error +} + +func (e IdGeneratorException) IdGeneratorException(message ...interface{}) { + fmt.Println(message) +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/IdGeneratorOptions.go b/code/allapp/allapp-go-v3/pkg/uniqueid/IdGeneratorOptions.go new file mode 100644 index 00000000..1f4ff6e1 --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/IdGeneratorOptions.go @@ -0,0 +1,25 @@ +package uniqueid + +type IdGeneratorOptions struct { + Method uint16 // 雪花计算方法,(1-漂移算法|2-传统算法),默认1 + BaseTime int64 // 基础时间(ms单位),不能超过当前系统时间 + WorkerId uint16 // 机器码,必须由外部设定,最大值 2^WorkerIdBitLength-1 + WorkerIdBitLength byte // 机器码位长,默认值6,取值范围 [1, 15](要求:序列数位长+机器码位长不超过22) + SeqBitLength byte // 序列数位长,默认值6,取值范围 [3, 21](要求:序列数位长+机器码位长不超过22) + MaxSeqNumber uint32 // 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值0,表示最大序列数取最大值(2^SeqBitLength-1]) + MinSeqNumber uint32 // 最小序列数(含),默认值5,取值范围 [5, MaxSeqNumber],每毫秒的前5个序列数对应编号0-4是保留位,其中1-4是时间回拨相应预留位,0是手工新值预留位 + TopOverCostCount uint32 // 最大漂移次数(含),默认2000,推荐范围500-10000(与计算能力有关) +} + +func NewIdGeneratorOptions(workerId uint16) *IdGeneratorOptions { + return &IdGeneratorOptions{ + Method: 1, + WorkerId: workerId, + BaseTime: 1582136402000, + WorkerIdBitLength: 6, + SeqBitLength: 6, + MaxSeqNumber: 0, + MinSeqNumber: 5, + TopOverCostCount: 2000, + } +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/IdHelper.go b/code/allapp/allapp-go-v3/pkg/uniqueid/IdHelper.go new file mode 100644 index 00000000..a76494a8 --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/IdHelper.go @@ -0,0 +1,29 @@ +package uniqueid + +import ( + "sync" +) + +var singletonMutex sync.Mutex +var idGenerator *DefaultIdGenerator + +// SetIdGenerator . +func SetIdGenerator(options *IdGeneratorOptions) { + singletonMutex.Lock() + idGenerator = NewDefaultIdGenerator(options) + singletonMutex.Unlock() +} + +// NextId . +func NextId() int64 { + if idGenerator == nil { + singletonMutex.Lock() + defer singletonMutex.Unlock() + if idGenerator == nil { + options := NewIdGeneratorOptions(1) + idGenerator = NewDefaultIdGenerator(options) + } + } + + return idGenerator.NewLong() +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/OverCostActionArg.go b/code/allapp/allapp-go-v3/pkg/uniqueid/OverCostActionArg.go new file mode 100644 index 00000000..5b8942cc --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/OverCostActionArg.go @@ -0,0 +1,19 @@ +package uniqueid + +type OverCostActionArg struct { + ActionType int32 + TimeTick int64 + WorkerId uint16 + OverCostCountInOneTerm int32 + GenCountInOneTerm int32 + TermIndex int32 +} + +func (ocaa OverCostActionArg) OverCostActionArg(workerId uint16, timeTick int64, actionType int32, overCostCountInOneTerm int32, genCountWhenOverCost int32, index int32) { + ocaa.ActionType = actionType + ocaa.TimeTick = timeTick + ocaa.WorkerId = workerId + ocaa.OverCostCountInOneTerm = overCostCountInOneTerm + ocaa.GenCountInOneTerm = genCountWhenOverCost + ocaa.TermIndex = index +} diff --git a/code/allapp/allapp-go-v3/pkg/uniqueid/SnowWorkerM1.go b/code/allapp/allapp-go-v3/pkg/uniqueid/SnowWorkerM1.go new file mode 100644 index 00000000..b9edde7f --- /dev/null +++ b/code/allapp/allapp-go-v3/pkg/uniqueid/SnowWorkerM1.go @@ -0,0 +1,243 @@ +package uniqueid + +import ( + "sync" + "time" +) + +// SnowWorkerM1 . +type SnowWorkerM1 struct { + BaseTime int64 //基础时间 + WorkerId uint16 //机器码 + WorkerIdBitLength byte //机器码位长 + SeqBitLength byte //自增序列数位长 + MaxSeqNumber uint32 //最大序列数(含) + MinSeqNumber uint32 //最小序列数(含) + TopOverCostCount uint32 //最大漂移次数 + _TimestampShift byte + _CurrentSeqNumber uint32 + + _LastTimeTick int64 + _TurnBackTimeTick int64 + _TurnBackIndex byte + _IsOverCost bool + _OverCostCountInOneTerm uint32 + _GenCountInOneTerm uint32 + _TermIndex uint32 + + sync.Mutex +} + +// NewSnowWorkerM1 . +func NewSnowWorkerM1(options *IdGeneratorOptions) ISnowWorker { + var workerIdBitLength byte + var seqBitLength byte + var maxSeqNumber uint32 + + // 1.BaseTime + var baseTime int64 + if options.BaseTime != 0 { + baseTime = options.BaseTime + } else { + baseTime = 1582136402000 + } + + // 2.WorkerIdBitLength + if options.WorkerIdBitLength == 0 { + workerIdBitLength = 6 + } else { + workerIdBitLength = options.WorkerIdBitLength + } + + // 3.WorkerId + var workerId = options.WorkerId + + // 4.SeqBitLength + if options.SeqBitLength == 0 { + seqBitLength = 6 + } else { + seqBitLength = options.SeqBitLength + } + + // 5.MaxSeqNumber + if options.MaxSeqNumber <= 0 { + maxSeqNumber = (1 << seqBitLength) - 1 + } else { + maxSeqNumber = options.MaxSeqNumber + } + + // 6.MinSeqNumber + var minSeqNumber = options.MinSeqNumber + + // 7.Others + var topOverCostCount = options.TopOverCostCount + if topOverCostCount == 0 { + topOverCostCount = 2000 + } + + timestampShift := (byte)(workerIdBitLength + seqBitLength) + currentSeqNumber := minSeqNumber + + return &SnowWorkerM1{ + BaseTime: baseTime, + WorkerIdBitLength: workerIdBitLength, + WorkerId: workerId, + SeqBitLength: seqBitLength, + MaxSeqNumber: maxSeqNumber, + MinSeqNumber: minSeqNumber, + TopOverCostCount: topOverCostCount, + _TimestampShift: timestampShift, + _CurrentSeqNumber: currentSeqNumber, + + _LastTimeTick: 0, + _TurnBackTimeTick: 0, + _TurnBackIndex: 0, + _IsOverCost: false, + _OverCostCountInOneTerm: 0, + _GenCountInOneTerm: 0, + _TermIndex: 0, + } +} + +// DoGenIDAction . +func (m1 *SnowWorkerM1) DoGenIdAction(arg *OverCostActionArg) { + +} + +func (m1 *SnowWorkerM1) BeginOverCostAction(useTimeTick int64) { + +} + +func (m1 *SnowWorkerM1) EndOverCostAction(useTimeTick int64) { + if m1._TermIndex > 10000 { + m1._TermIndex = 0 + } +} + +func (m1 *SnowWorkerM1) BeginTurnBackAction(useTimeTick int64) { + +} + +func (m1 *SnowWorkerM1) EndTurnBackAction(useTimeTick int64) { + +} + +func (m1 *SnowWorkerM1) NextOverCostId() int64 { + currentTimeTick := m1.GetCurrentTimeTick() + if currentTimeTick > m1._LastTimeTick { + m1.EndOverCostAction(currentTimeTick) + m1._LastTimeTick = currentTimeTick + m1._CurrentSeqNumber = m1.MinSeqNumber + m1._IsOverCost = false + m1._OverCostCountInOneTerm = 0 + m1._GenCountInOneTerm = 0 + return m1.CalcId(m1._LastTimeTick) + } + if m1._OverCostCountInOneTerm >= m1.TopOverCostCount { + m1.EndOverCostAction(currentTimeTick) + m1._LastTimeTick = m1.GetNextTimeTick() + m1._CurrentSeqNumber = m1.MinSeqNumber + m1._IsOverCost = false + m1._OverCostCountInOneTerm = 0 + m1._GenCountInOneTerm = 0 + return m1.CalcId(m1._LastTimeTick) + } + if m1._CurrentSeqNumber > m1.MaxSeqNumber { + m1._LastTimeTick++ + m1._CurrentSeqNumber = m1.MinSeqNumber + m1._IsOverCost = true + m1._OverCostCountInOneTerm++ + m1._GenCountInOneTerm++ + + return m1.CalcId(m1._LastTimeTick) + } + + m1._GenCountInOneTerm++ + return m1.CalcId(m1._LastTimeTick) +} + +// NextNormalID . +func (m1 *SnowWorkerM1) NextNormalId() int64 { + currentTimeTick := m1.GetCurrentTimeTick() + if currentTimeTick < m1._LastTimeTick { + if m1._TurnBackTimeTick < 1 { + m1._TurnBackTimeTick = m1._LastTimeTick - 1 + m1._TurnBackIndex++ + // 每毫秒序列数的前5位是预留位,0用于手工新值,1-4是时间回拨次序 + // 最多4次回拨(防止回拨重叠) + if m1._TurnBackIndex > 4 { + m1._TurnBackIndex = 1 + } + m1.BeginTurnBackAction(m1._TurnBackTimeTick) + } + + // time.Sleep(time.Duration(1) * time.Millisecond) + return m1.CalcTurnBackId(m1._TurnBackTimeTick) + } + + // 时间追平时,_TurnBackTimeTick清零 + if m1._TurnBackTimeTick > 0 { + m1.EndTurnBackAction(m1._TurnBackTimeTick) + m1._TurnBackTimeTick = 0 + } + + if currentTimeTick > m1._LastTimeTick { + m1._LastTimeTick = currentTimeTick + m1._CurrentSeqNumber = m1.MinSeqNumber + return m1.CalcId(m1._LastTimeTick) + } + + if m1._CurrentSeqNumber > m1.MaxSeqNumber { + m1.BeginOverCostAction(currentTimeTick) + m1._TermIndex++ + m1._LastTimeTick++ + m1._CurrentSeqNumber = m1.MinSeqNumber + m1._IsOverCost = true + m1._OverCostCountInOneTerm = 1 + m1._GenCountInOneTerm = 1 + + return m1.CalcId(m1._LastTimeTick) + } + + return m1.CalcId(m1._LastTimeTick) +} + +// CalcID . +func (m1 *SnowWorkerM1) CalcId(useTimeTick int64) int64 { + result := int64(useTimeTick< m2.MaxSeqNumber { + m2._CurrentSeqNumber = m2.MinSeqNumber + currentTimeTick = m2.GetNextTimeTick() + } + } else { + m2._CurrentSeqNumber = m2.MinSeqNumber + } + if currentTimeTick < m2._LastTimeTick { + fmt.Println("Time error for {0} milliseconds", strconv.FormatInt(m2._LastTimeTick-currentTimeTick, 10)) + } + m2._LastTimeTick = currentTimeTick + result := int64(currentTimeTick<