A:数据库初始化,并增加User表示例
This commit is contained in:
1 parent
da2222f2d9
commit
b8dc199cff
15 files changed
+1459
-108
No files matched your search
@@ -8,10 +8,14 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
|
gorm.io/driver/mysql v1.6.0
|
||||||
|
gorm.io/driver/sqlite v1.6.0
|
||||||
|
gorm.io/gorm v1.30.0
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/bytedance/sonic v1.13.3 // indirect
|
github.com/bytedance/sonic v1.13.3 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
@@ -20,11 +24,15 @@ require (
|
|||||||
github.com/go-playground/locales v0.14.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/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
|||||||
+25
-93
@@ -1,187 +1,119 @@
|
|||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A=
|
|
||||||
github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
|
||||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||||
github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs=
|
|
||||||
github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg=
|
|
||||||
github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro=
|
|
||||||
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
||||||
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
|
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
|
||||||
github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90=
|
|
||||||
github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcvgM=
|
github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcvgM=
|
||||||
github.com/gin-contrib/pprof v1.5.3/go.mod h1:0+LQSZ4SLO0B6+2n6JBzaEygpTBxe/nI+YEYpfQQ6xY=
|
github.com/gin-contrib/pprof v1.5.3/go.mod h1:0+LQSZ4SLO0B6+2n6JBzaEygpTBxe/nI+YEYpfQQ6xY=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
|
||||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
|
||||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
|
||||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
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 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
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/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
|
||||||
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
|
|
||||||
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
|
||||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
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/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.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.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.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.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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
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/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
|
||||||
github.com/ugorji/go/codec v1.2.14 h1:yOQvXCBc3Ij46LRkRoh4Yd5qK6LVOgi0bYOXfb7ifjw=
|
github.com/ugorji/go/codec v1.2.14 h1:yOQvXCBc3Ij46LRkRoh4Yd5qK6LVOgi0bYOXfb7ifjw=
|
||||||
github.com/ugorji/go/codec v1.2.14/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.14/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
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/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
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/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
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/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
|
||||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
|
||||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
|
||||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
|
||||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
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/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||||
|
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||||
|
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||||
|
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package model
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"rear/internal/utils"
|
"rear/internal/utils"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DatabaseConfig 数据库配置
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Type DatabaseType
|
||||||
|
// Mysql 属性
|
||||||
|
Host string
|
||||||
|
// Mysql 属性
|
||||||
|
Port string
|
||||||
|
Database string
|
||||||
|
// Mysql 属性
|
||||||
|
Username string
|
||||||
|
// Mysql 属性
|
||||||
|
Password string
|
||||||
|
// SQLite specific
|
||||||
|
DBPath string
|
||||||
|
MaxIdleConns int
|
||||||
|
MaxOpenConns int
|
||||||
|
MaxLifetime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseType 数据库类型
|
||||||
|
type DatabaseType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SQLite DatabaseType = "sqlite"
|
||||||
|
MySQL DatabaseType = "mysql"
|
||||||
|
)
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
"rear/internal/config"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteTask 写任务接口
|
||||||
|
type WriteTask interface {
|
||||||
|
Execute(db *gorm.DB) error
|
||||||
|
GetPriority() int // 优先级,用于任务排序
|
||||||
|
GetID() string // 任务ID,用于去重或追踪
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTaskResult 写任务结果
|
||||||
|
type WriteTaskResult struct {
|
||||||
|
TaskID string
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskCallback 任务回调
|
||||||
|
type TaskCallback struct {
|
||||||
|
OnSuccess func(taskID string)
|
||||||
|
OnError func(taskID string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseManager 数据库管理器
|
||||||
|
type DatabaseManager struct {
|
||||||
|
db *gorm.DB
|
||||||
|
dbType config.DatabaseType
|
||||||
|
|
||||||
|
// 写任务队列
|
||||||
|
writeQueue chan WriteTaskWithCallback
|
||||||
|
resultQueue chan WriteTaskResult
|
||||||
|
stopCh chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
// 读写分离配置
|
||||||
|
maxWriteWorkers int
|
||||||
|
maxRetries int
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTaskWithCallback 带回调的写任务
|
||||||
|
type WriteTaskWithCallback struct {
|
||||||
|
Task WriteTask
|
||||||
|
Callback TaskCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDatabaseManager 创建数据库管理器
|
||||||
|
func NewDatabaseManager(db *gorm.DB, dbType config.DatabaseType) *DatabaseManager {
|
||||||
|
maxWorkers := 1 // SQLite默认单线程
|
||||||
|
if dbType == config.MySQL {
|
||||||
|
maxWorkers = 10 // MySQL可以多线程
|
||||||
|
}
|
||||||
|
|
||||||
|
dm := &DatabaseManager{
|
||||||
|
db: db,
|
||||||
|
dbType: dbType,
|
||||||
|
writeQueue: make(chan WriteTaskWithCallback, 1000), // 缓冲队列
|
||||||
|
resultQueue: make(chan WriteTaskResult, 1000),
|
||||||
|
stopCh: make(chan struct{}),
|
||||||
|
maxWriteWorkers: maxWorkers,
|
||||||
|
maxRetries: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
dm.start()
|
||||||
|
return dm
|
||||||
|
}
|
||||||
|
|
||||||
|
// start 启动数据库管理器
|
||||||
|
func (dm *DatabaseManager) start() {
|
||||||
|
// 启动写任务处理器
|
||||||
|
for i := 0; i < dm.maxWriteWorkers; i++ {
|
||||||
|
dm.wg.Add(1)
|
||||||
|
go dm.writeWorker(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动结果处理器
|
||||||
|
dm.wg.Add(1)
|
||||||
|
go dm.resultProcessor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeWorker 写任务工作协程
|
||||||
|
func (dm *DatabaseManager) writeWorker(workerID int) {
|
||||||
|
defer dm.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case taskWithCallback := <-dm.writeQueue:
|
||||||
|
dm.executeWriteTask(taskWithCallback)
|
||||||
|
case <-dm.stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeWriteTask 执行写任务
|
||||||
|
func (dm *DatabaseManager) executeWriteTask(taskWithCallback WriteTaskWithCallback) {
|
||||||
|
task := taskWithCallback.Task
|
||||||
|
callback := taskWithCallback.Callback
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for retry := 0; retry < dm.maxRetries; retry++ {
|
||||||
|
// 对于SQLite,可以使用事务来优化性能
|
||||||
|
if dm.dbType == config.SQLite {
|
||||||
|
err = dm.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
return task.Execute(tx)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
err = task.Execute(dm.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重试延迟
|
||||||
|
if retry < dm.maxRetries-1 {
|
||||||
|
time.Sleep(time.Millisecond * time.Duration(100*(retry+1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送结果
|
||||||
|
result := WriteTaskResult{
|
||||||
|
TaskID: task.GetID(),
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case dm.resultQueue <- result:
|
||||||
|
default:
|
||||||
|
// 结果队列满了,直接执行回调
|
||||||
|
if err != nil && callback.OnError != nil {
|
||||||
|
callback.OnError(task.GetID(), err)
|
||||||
|
} else if err == nil && callback.OnSuccess != nil {
|
||||||
|
callback.OnSuccess(task.GetID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resultProcessor 结果处理器
|
||||||
|
func (dm *DatabaseManager) resultProcessor() {
|
||||||
|
defer dm.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case result := <-dm.resultQueue:
|
||||||
|
// 这里可以进行结果处理,比如记录日志、监控等
|
||||||
|
fmt.Printf("Task %s completed with error: %v\n", result.TaskID, result.Error)
|
||||||
|
case <-dm.stopCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitWriteTask 提交写任务
|
||||||
|
func (dm *DatabaseManager) SubmitWriteTask(task WriteTask, callback TaskCallback) error {
|
||||||
|
taskWithCallback := WriteTaskWithCallback{
|
||||||
|
Task: task,
|
||||||
|
Callback: callback,
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case dm.writeQueue <- taskWithCallback:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("write queue is full")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitWriteTaskSync 同步提交写任务
|
||||||
|
func (dm *DatabaseManager) SubmitWriteTaskSync(ctx context.Context, task WriteTask) error {
|
||||||
|
done := make(chan error, 1)
|
||||||
|
|
||||||
|
callback := TaskCallback{
|
||||||
|
OnSuccess: func(taskID string) {
|
||||||
|
done <- nil
|
||||||
|
},
|
||||||
|
OnError: func(taskID string, err error) {
|
||||||
|
done <- err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dm.SubmitWriteTask(task, callback); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-done:
|
||||||
|
return err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadDirect 直接读取(不经过队列)
|
||||||
|
func (dm *DatabaseManager) ReadDirect() *gorm.DB {
|
||||||
|
return dm.db
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDB 获取数据库连接(仅用于读操作)
|
||||||
|
func (dm *DatabaseManager) GetDB() *gorm.DB {
|
||||||
|
return dm.db
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop 停止数据库管理器
|
||||||
|
func (dm *DatabaseManager) Stop() {
|
||||||
|
close(dm.stopCh)
|
||||||
|
dm.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDatabase() error {
|
||||||
|
databaseConfig := getDatabaseConfig()
|
||||||
|
|
||||||
|
var db *gorm.DB
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch databaseConfig.Type {
|
||||||
|
case config.MySQL:
|
||||||
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
|
databaseConfig.Username, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Database)
|
||||||
|
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
case config.SQLite:
|
||||||
|
// SQLite特殊配置
|
||||||
|
db, err = gorm.Open(sqlite.Open(databaseConfig.Database), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Info),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQLite优化设置
|
||||||
|
sqlDB, _ := db.DB()
|
||||||
|
sqlDB.SetMaxIdleConns(1) // SQLite单连接
|
||||||
|
sqlDB.SetMaxOpenConns(1) // SQLite单连接
|
||||||
|
sqlDB.SetConnMaxLifetime(0) // 永不关闭
|
||||||
|
|
||||||
|
// SQLite WAL模式优化
|
||||||
|
db.Exec("PRAGMA journal_mode=WAL;")
|
||||||
|
db.Exec("PRAGMA synchronous=NORMAL;")
|
||||||
|
db.Exec("PRAGMA cache_size=1000000;")
|
||||||
|
db.Exec("PRAGMA foreign_keys=true;")
|
||||||
|
db.Exec("PRAGMA temp_store=memory;")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported database type: %s", databaseConfig.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect database: %w", err)
|
||||||
|
}
|
||||||
|
DB = db
|
||||||
|
Manger = NewDatabaseManager(db, databaseConfig.Type)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDatabaseConfig() config.DatabaseConfig {
|
||||||
|
// 初始化数据库
|
||||||
|
return config.DatabaseConfig{
|
||||||
|
Type: config.SQLite,
|
||||||
|
Database: "test.db",
|
||||||
|
MaxIdleConns: 1,
|
||||||
|
MaxOpenConns: 1,
|
||||||
|
MaxLifetime: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var DB *gorm.DB
|
||||||
|
var Manger *DatabaseManager
|
||||||
|
|
||||||
|
func GetDB() *gorm.DB {
|
||||||
|
return DB
|
||||||
|
}
|
||||||
|
func GetManger() *DatabaseManager {
|
||||||
|
return Manger
|
||||||
|
}
|
||||||
|
func IsSQLite() bool {
|
||||||
|
cfg := getDatabaseConfig()
|
||||||
|
return cfg.Type == config.SQLite
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import "rear/internal/config"
|
||||||
|
|
||||||
|
// OptimizedDatabaseManager 优化版数据库管理器
|
||||||
|
type OptimizedDatabaseManager struct {
|
||||||
|
*DatabaseManager
|
||||||
|
config config.DatabaseConfig
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"rear/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AutoMigrate() error {
|
||||||
|
return DB.AutoMigrate(
|
||||||
|
&model.User{},
|
||||||
|
// 在这里添加其他模型
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -3,8 +3,10 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"rear/internal/model"
|
"rear/internal/model"
|
||||||
|
"rear/internal/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 全局变量
|
// 全局变量
|
||||||
@@ -58,8 +60,8 @@ func GetUserByID(c *gin.Context) {
|
|||||||
// InitSampleData 初始化示例数据
|
// InitSampleData 初始化示例数据
|
||||||
func InitSampleData() {
|
func InitSampleData() {
|
||||||
Users = []model.User{
|
Users = []model.User{
|
||||||
{ID: 1, Name: "John Doe", Email: "john@example.com"},
|
{Username: "John Doe", Email: "john@example.com"},
|
||||||
{ID: 2, Name: "Jane Smith", Email: "jane@example.com"},
|
{Username: "Jane Smith", Email: "jane@example.com"},
|
||||||
}
|
}
|
||||||
userNextID = 3
|
userNextID = 3
|
||||||
}
|
}
|
||||||
@@ -99,11 +101,26 @@ func UpdateUser(c *gin.Context) {
|
|||||||
|
|
||||||
// GetUsers 获取所有用户
|
// GetUsers 获取所有用户
|
||||||
func GetUsers(c *gin.Context) {
|
func GetUsers(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, model.Response{
|
// 使用示例
|
||||||
Code: http.StatusOK,
|
userService := repositories.NewUserService()
|
||||||
Message: "Success",
|
|
||||||
Data: Users,
|
if users, err := userService.GetAllUsers(); err != nil {
|
||||||
})
|
log.Printf("Failed to get all users: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Total users: %d", len(users))
|
||||||
|
c.JSON(http.StatusOK, model.Response{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
Message: "Success",
|
||||||
|
Data: users,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//c.JSON(http.StatusOK, model.Response{
|
||||||
|
// Code: http.StatusOK,
|
||||||
|
// Message: "Success",
|
||||||
|
// Data: Users,
|
||||||
|
//})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser 创建用户
|
// CreateUser 创建用户
|
||||||
@@ -118,7 +135,7 @@ func CreateUser(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 简单验证
|
// 简单验证
|
||||||
if user.Name == "" || user.Email == "" {
|
if user.Username == "" || user.Email == "" {
|
||||||
c.JSON(http.StatusBadRequest, model.Response{
|
c.JSON(http.StatusBadRequest, model.Response{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
Message: "Name and email are required",
|
Message: "Name and email are required",
|
||||||
@@ -126,7 +143,6 @@ func CreateUser(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user.ID = userNextID
|
|
||||||
userNextID++
|
userNextID++
|
||||||
Users = append(Users, user)
|
Users = append(Users, user)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseModel 基础模型,包含通用字段
|
||||||
|
type BaseModel struct {
|
||||||
|
ID uint `gorm:"primaryKey" json:"id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
IsDeleted bool `json:"is_deleted"`
|
||||||
|
}
|
||||||
@@ -1,8 +1,29 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
// User 示例用户结构
|
import "gorm.io/gorm"
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `json:"id"`
|
BaseModel
|
||||||
Name string `json:"name"`
|
Username string `gorm:"uniqueIndex;not null;size:50" json:"username"`
|
||||||
Email string `json:"email"`
|
Email string `gorm:"uniqueIndex;not null;size:100" json:"email"`
|
||||||
|
Password string `gorm:"not null;size:255" json:"-"`
|
||||||
|
Age int `gorm:"default:0" json:"age"`
|
||||||
|
Status int `gorm:"default:1;comment:1-active,0-inactive" json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName 指定表名
|
||||||
|
func (User) GetTableName() string {
|
||||||
|
return "users"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeCreate GORM钩子,创建前执行
|
||||||
|
func (u *User) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
// 这里可以添加创建前的逻辑,比如密码加密
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterCreate GORM钩子,创建后执行
|
||||||
|
func (u *User) AfterCreate(tx *gorm.DB) error {
|
||||||
|
// 这里可以添加创建后的逻辑
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"rear/internal/db"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 写操作类型
|
||||||
|
type WriteOperation struct {
|
||||||
|
Execute func() error
|
||||||
|
Response chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseService 处理数据库写操作的单线程执行
|
||||||
|
type BaseService struct {
|
||||||
|
writeQueue chan WriteOperation
|
||||||
|
once sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseService = &BaseService{
|
||||||
|
writeQueue: make(chan WriteOperation, 1000),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化并启动写操作处理协程
|
||||||
|
func InitBaseService() {
|
||||||
|
baseService.once.Do(func() {
|
||||||
|
go baseService.processWriteOperations()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理写操作的协程
|
||||||
|
func (s *BaseService) processWriteOperations() {
|
||||||
|
for op := range s.writeQueue {
|
||||||
|
err := op.Execute()
|
||||||
|
op.Response <- err
|
||||||
|
close(op.Response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteWrite 执行写操作(增删改)
|
||||||
|
func ExecuteWrite(fn func() error) error {
|
||||||
|
// 如果不是 SQLite,直接执行
|
||||||
|
if !db.IsSQLite() {
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SQLite 走单线程队列
|
||||||
|
op := WriteOperation{
|
||||||
|
Execute: fn,
|
||||||
|
Response: make(chan error, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
baseService.writeQueue <- op
|
||||||
|
return <-op.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteRead 执行读操作(可以并发)
|
||||||
|
func ExecuteRead(fn func() error) error {
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"rear/internal/db"
|
||||||
|
"rear/internal/model"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService struct{}
|
||||||
|
|
||||||
|
func NewUserService() *UserService {
|
||||||
|
return &UserService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建用户(写操作)
|
||||||
|
func (s *UserService) CreateUser(user *model.User) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Create(user).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户(写操作)
|
||||||
|
func (s *UserService) UpdateUser(id uint, updates map[string]interface{}) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Model(&model.User{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Updates(updates).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户名称(写操作)
|
||||||
|
func (s *UserService) UpdateUserName(id uint, name string) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Model(&model.User{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Update("name", name).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户(写操作)
|
||||||
|
func (s *UserService) DeleteUser(id uint) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Delete(&model.User{}, id).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 软删除用户(写操作)
|
||||||
|
func (s *UserService) SoftDeleteUser(id uint) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Delete(&model.User{}, id).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量创建用户(写操作)
|
||||||
|
func (s *UserService) CreateUsers(users []model.User) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Create(&users).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 读操作(可以并发)============
|
||||||
|
|
||||||
|
// 根据 ID 获取用户
|
||||||
|
func (s *UserService) GetUserByID(id uint) (*model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
err := ExecuteRead(func() error {
|
||||||
|
return db.GetDB().First(&user, id).Error
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户名获取用户
|
||||||
|
func (s *UserService) GetUserByUsername(username string) (*model.User, error) {
|
||||||
|
var user model.User
|
||||||
|
err := ExecuteRead(func() error {
|
||||||
|
return db.GetDB().Where("username = ?", username).First(&user).Error
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有用户
|
||||||
|
func (s *UserService) GetAllUsers() ([]model.User, error) {
|
||||||
|
var users []model.User
|
||||||
|
err := ExecuteRead(func() error {
|
||||||
|
return db.GetDB().Find(&users).Error
|
||||||
|
})
|
||||||
|
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页获取用户
|
||||||
|
func (s *UserService) GetUsersPaginated(offset, limit int) ([]model.User, int64, error) {
|
||||||
|
var users []model.User
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
err := ExecuteRead(func() error {
|
||||||
|
// 获取总数
|
||||||
|
if err := db.GetDB().Model(&model.User{}).Count(&total).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取分页数据
|
||||||
|
return db.GetDB().Offset(offset).Limit(limit).Find(&users).Error
|
||||||
|
})
|
||||||
|
|
||||||
|
return users, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据条件查询用户
|
||||||
|
func (s *UserService) GetUsersByCondition(condition map[string]interface{}) ([]model.User, error) {
|
||||||
|
var users []model.User
|
||||||
|
err := ExecuteRead(func() error {
|
||||||
|
query := db.GetDB()
|
||||||
|
for key, value := range condition {
|
||||||
|
query = query.Where(key+" = ?", value)
|
||||||
|
}
|
||||||
|
return query.Find(&users).Error
|
||||||
|
})
|
||||||
|
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事务示例(写操作)
|
||||||
|
func (s *UserService) CreateUserWithTransaction(user *model.User, callback func(tx *gorm.DB) error) error {
|
||||||
|
return ExecuteWrite(func() error {
|
||||||
|
return db.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
// 创建用户
|
||||||
|
if err := tx.Create(user).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行其他操作
|
||||||
|
if callback != nil {
|
||||||
|
return callback(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,400 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Task 任务接口
|
||||||
|
type Task interface {
|
||||||
|
// Execute 执行任务
|
||||||
|
Execute(ctx context.Context) error
|
||||||
|
// ID 获取任务ID
|
||||||
|
ID() string
|
||||||
|
// Type 获取任务类型
|
||||||
|
Type() string
|
||||||
|
|
||||||
|
/*
|
||||||
|
紧急任务:优先级 8-10
|
||||||
|
正常任务:优先级 4-7
|
||||||
|
后台任务:优先级 1-3
|
||||||
|
*/
|
||||||
|
// Priority 获取任务优先级 (数字越大优先级越高)
|
||||||
|
Priority() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskResult 任务执行结果
|
||||||
|
type TaskResult struct {
|
||||||
|
TaskID string
|
||||||
|
TaskType string
|
||||||
|
Success bool
|
||||||
|
Error error
|
||||||
|
Duration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskScheduler 任务调度器
|
||||||
|
type TaskScheduler struct {
|
||||||
|
workers int
|
||||||
|
taskQueue chan Task
|
||||||
|
resultQueue chan TaskResult
|
||||||
|
workerPool chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// 统计信息
|
||||||
|
totalTasks int64
|
||||||
|
completedTasks int64
|
||||||
|
failedTasks int64
|
||||||
|
|
||||||
|
// 任务类型统计
|
||||||
|
taskStats map[string]*TaskTypeStats
|
||||||
|
statsMutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskTypeStats 任务类型统计
|
||||||
|
type TaskTypeStats struct {
|
||||||
|
Total int64
|
||||||
|
Completed int64
|
||||||
|
Failed int64
|
||||||
|
AvgTime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTaskScheduler 创建任务调度器
|
||||||
|
func NewTaskScheduler(workers int, queueSize int) *TaskScheduler {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
ts := &TaskScheduler{
|
||||||
|
workers: workers,
|
||||||
|
taskQueue: make(chan Task, queueSize),
|
||||||
|
resultQueue: make(chan TaskResult, workers),
|
||||||
|
workerPool: make(chan struct{}, workers),
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
taskStats: make(map[string]*TaskTypeStats),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化worker池
|
||||||
|
for i := 0; i < workers; i++ {
|
||||||
|
ts.workerPool <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动worker
|
||||||
|
ts.startWorkers()
|
||||||
|
|
||||||
|
// 启动结果处理器
|
||||||
|
go ts.resultProcessor()
|
||||||
|
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
// startWorkers 启动worker
|
||||||
|
func (ts *TaskScheduler) startWorkers() {
|
||||||
|
for i := 0; i < ts.workers; i++ {
|
||||||
|
ts.wg.Add(1)
|
||||||
|
go ts.worker(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// worker 工作协程
|
||||||
|
func (ts *TaskScheduler) worker(id int) {
|
||||||
|
defer ts.wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ts.ctx.Done():
|
||||||
|
return
|
||||||
|
case task, ok := <-ts.taskQueue:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取worker令牌
|
||||||
|
<-ts.workerPool
|
||||||
|
|
||||||
|
// 执行任务
|
||||||
|
start := time.Now()
|
||||||
|
err := task.Execute(ts.ctx)
|
||||||
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
// 发送结果
|
||||||
|
ts.resultQueue <- TaskResult{
|
||||||
|
TaskID: task.ID(),
|
||||||
|
TaskType: task.Type(),
|
||||||
|
Success: err == nil,
|
||||||
|
Error: err,
|
||||||
|
Duration: duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 归还worker令牌
|
||||||
|
ts.workerPool <- struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resultProcessor 结果处理器
|
||||||
|
func (ts *TaskScheduler) resultProcessor() {
|
||||||
|
for result := range ts.resultQueue {
|
||||||
|
atomic.AddInt64(&ts.completedTasks, 1)
|
||||||
|
|
||||||
|
if !result.Success {
|
||||||
|
atomic.AddInt64(&ts.failedTasks, 1)
|
||||||
|
log.Printf("Task %s failed: %v", result.TaskID, result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新统计信息
|
||||||
|
ts.updateStats(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateStats 更新统计信息
|
||||||
|
func (ts *TaskScheduler) updateStats(result TaskResult) {
|
||||||
|
ts.statsMutex.Lock()
|
||||||
|
defer ts.statsMutex.Unlock()
|
||||||
|
|
||||||
|
stats, exists := ts.taskStats[result.TaskType]
|
||||||
|
if !exists {
|
||||||
|
stats = &TaskTypeStats{}
|
||||||
|
ts.taskStats[result.TaskType] = stats
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.Total++
|
||||||
|
if result.Success {
|
||||||
|
stats.Completed++
|
||||||
|
} else {
|
||||||
|
stats.Failed++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新平均执行时间
|
||||||
|
if stats.Completed > 0 {
|
||||||
|
stats.AvgTime = time.Duration(
|
||||||
|
(int64(stats.AvgTime)*(stats.Completed-1) + int64(result.Duration)) / stats.Completed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit 提交任务
|
||||||
|
func (ts *TaskScheduler) Submit(task Task) error {
|
||||||
|
select {
|
||||||
|
case <-ts.ctx.Done():
|
||||||
|
return fmt.Errorf("scheduler is shutting down")
|
||||||
|
case ts.taskQueue <- task:
|
||||||
|
atomic.AddInt64(&ts.totalTasks, 1)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("task queue is full")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitBatch 批量提交任务
|
||||||
|
func (ts *TaskScheduler) SubmitBatch(tasks []Task) error {
|
||||||
|
for _, task := range tasks {
|
||||||
|
if err := ts.Submit(task); err != nil {
|
||||||
|
return fmt.Errorf("failed to submit task %s: %w", task.ID(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStats 获取统计信息
|
||||||
|
func (ts *TaskScheduler) GetStats() map[string]interface{} {
|
||||||
|
ts.statsMutex.RLock()
|
||||||
|
defer ts.statsMutex.RUnlock()
|
||||||
|
|
||||||
|
stats := make(map[string]interface{})
|
||||||
|
stats["total_tasks"] = atomic.LoadInt64(&ts.totalTasks)
|
||||||
|
stats["completed_tasks"] = atomic.LoadInt64(&ts.completedTasks)
|
||||||
|
stats["failed_tasks"] = atomic.LoadInt64(&ts.failedTasks)
|
||||||
|
stats["pending_tasks"] = len(ts.taskQueue)
|
||||||
|
stats["active_workers"] = ts.workers - len(ts.workerPool)
|
||||||
|
|
||||||
|
// 复制任务类型统计
|
||||||
|
typeStats := make(map[string]*TaskTypeStats)
|
||||||
|
for k, v := range ts.taskStats {
|
||||||
|
typeStats[k] = &TaskTypeStats{
|
||||||
|
Total: v.Total,
|
||||||
|
Completed: v.Completed,
|
||||||
|
Failed: v.Failed,
|
||||||
|
AvgTime: v.AvgTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats["type_stats"] = typeStats
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown 关闭调度器
|
||||||
|
func (ts *TaskScheduler) Shutdown() {
|
||||||
|
ts.cancel()
|
||||||
|
close(ts.taskQueue)
|
||||||
|
ts.wg.Wait()
|
||||||
|
close(ts.resultQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 具体任务实现示例 ---
|
||||||
|
|
||||||
|
// ImageCompressTask 图片压缩任务
|
||||||
|
type ImageCompressTask struct {
|
||||||
|
id string
|
||||||
|
filePath string
|
||||||
|
quality int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImageCompressTask(id, filePath string, quality int) *ImageCompressTask {
|
||||||
|
return &ImageCompressTask{
|
||||||
|
id: id,
|
||||||
|
filePath: filePath,
|
||||||
|
quality: quality,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ImageCompressTask) Execute(ctx context.Context) error {
|
||||||
|
// 这里是实际的图片压缩逻辑
|
||||||
|
// 可以使用 imaging 库或调用外部工具
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
// 模拟压缩操作
|
||||||
|
// 实际实现中,这里会调用图片处理库
|
||||||
|
log.Printf("Compressing image: %s with quality %d", t.filePath, t.quality)
|
||||||
|
time.Sleep(100 * time.Millisecond) // 模拟耗时操作
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ImageCompressTask) ID() string { return t.id }
|
||||||
|
func (t *ImageCompressTask) Type() string { return "image_compress" }
|
||||||
|
func (t *ImageCompressTask) Priority() int { return 5 }
|
||||||
|
|
||||||
|
// ExifReadTask EXIF读取任务
|
||||||
|
type ExifReadTask struct {
|
||||||
|
id string
|
||||||
|
filePath string
|
||||||
|
exifData map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExifReadTask(id, filePath string) *ExifReadTask {
|
||||||
|
return &ExifReadTask{
|
||||||
|
id: id,
|
||||||
|
filePath: filePath,
|
||||||
|
exifData: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ExifReadTask) Execute(ctx context.Context) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
// 使用 exiftool 命令行工具
|
||||||
|
cmd := exec.CommandContext(ctx, "exiftool", "-j", t.filePath)
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("exiftool failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里应该解析 JSON 输出到 t.exifData
|
||||||
|
log.Printf("Read EXIF from: %s, data size: %d", t.filePath, len(output))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ExifReadTask) ID() string { return t.id }
|
||||||
|
func (t *ExifReadTask) Type() string { return "exif_read" }
|
||||||
|
func (t *ExifReadTask) Priority() int { return 3 }
|
||||||
|
|
||||||
|
// PriorityTaskScheduler 支持优先级的任务调度器
|
||||||
|
type PriorityTaskScheduler struct {
|
||||||
|
*TaskScheduler
|
||||||
|
priorityQueue *PriorityQueue
|
||||||
|
queueMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// PriorityQueue 优先级队列实现
|
||||||
|
type PriorityQueue struct {
|
||||||
|
tasks []Task
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pq *PriorityQueue) Push(task Task) {
|
||||||
|
pq.tasks = append(pq.tasks, task)
|
||||||
|
// 简单的插入排序,实际可以使用堆
|
||||||
|
for i := len(pq.tasks) - 1; i > 0; i-- {
|
||||||
|
if pq.tasks[i].Priority() > pq.tasks[i-1].Priority() {
|
||||||
|
pq.tasks[i], pq.tasks[i-1] = pq.tasks[i-1], pq.tasks[i]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pq *PriorityQueue) Pop() Task {
|
||||||
|
if len(pq.tasks) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
task := pq.tasks[0]
|
||||||
|
pq.tasks = pq.tasks[1:]
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pq *PriorityQueue) Len() int {
|
||||||
|
return len(pq.tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPriorityTaskScheduler 创建支持优先级的调度器
|
||||||
|
func NewPriorityTaskScheduler(workers int) *PriorityTaskScheduler {
|
||||||
|
pts := &PriorityTaskScheduler{
|
||||||
|
TaskScheduler: NewTaskScheduler(workers, 0), // 不使用内置队列
|
||||||
|
priorityQueue: &PriorityQueue{tasks: make([]Task, 0)},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动优先级调度协程
|
||||||
|
go pts.priorityDispatcher()
|
||||||
|
|
||||||
|
return pts
|
||||||
|
}
|
||||||
|
|
||||||
|
// priorityDispatcher 优先级调度器
|
||||||
|
func (pts *PriorityTaskScheduler) priorityDispatcher() {
|
||||||
|
ticker := time.NewTicker(10 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-pts.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
pts.queueMutex.Lock()
|
||||||
|
if pts.priorityQueue.Len() > 0 && len(pts.taskQueue) < cap(pts.taskQueue) {
|
||||||
|
task := pts.priorityQueue.Pop()
|
||||||
|
select {
|
||||||
|
case pts.taskQueue <- task:
|
||||||
|
// 成功发送
|
||||||
|
default:
|
||||||
|
// 队列满了,放回优先级队列
|
||||||
|
pts.priorityQueue.Push(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pts.queueMutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitWithPriority 提交带优先级的任务
|
||||||
|
func (pts *PriorityTaskScheduler) SubmitWithPriority(task Task) error {
|
||||||
|
select {
|
||||||
|
case <-pts.ctx.Done():
|
||||||
|
return fmt.Errorf("scheduler is shutting down")
|
||||||
|
default:
|
||||||
|
pts.queueMutex.Lock()
|
||||||
|
pts.priorityQueue.Push(task)
|
||||||
|
atomic.AddInt64(&pts.totalTasks, 1)
|
||||||
|
pts.queueMutex.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 示例:批量处理图片任务
|
||||||
|
func processImages(scheduler *TaskScheduler) {
|
||||||
|
// 假设有10000张图片需要处理
|
||||||
|
imagePaths := make([]string, 10000)
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
imagePaths[i] = fmt.Sprintf("/path/to/image_%d.jpg", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量提交压缩任务
|
||||||
|
log.Println("提交图片压缩任务...")
|
||||||
|
for i, path := range imagePaths {
|
||||||
|
task := NewImageCompressTask(
|
||||||
|
fmt.Sprintf("compress_%d", i),
|
||||||
|
path,
|
||||||
|
85, // 压缩质量
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := scheduler.Submit(task); err != nil {
|
||||||
|
log.Printf("Failed to submit task: %v", err)
|
||||||
|
// 可以实现重试逻辑
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
scheduler.Submit(task) // 重试一次
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时提交EXIF读取任务
|
||||||
|
log.Println("提交EXIF读取任务...")
|
||||||
|
for i, path := range imagePaths[:1000] { // 只处理前1000张
|
||||||
|
task := NewExifReadTask(
|
||||||
|
fmt.Sprintf("exif_%d", i),
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
scheduler.Submit(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 高级任务管理器 - 支持任务组和依赖
|
||||||
|
type TaskManager struct {
|
||||||
|
scheduler *PriorityTaskScheduler
|
||||||
|
taskGroups map[string]*TaskGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskGroup struct {
|
||||||
|
ID string
|
||||||
|
Tasks []Task
|
||||||
|
Status string
|
||||||
|
Progress float64
|
||||||
|
StartTime time.Time
|
||||||
|
EndTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTaskManager(workers int) *TaskManager {
|
||||||
|
return &TaskManager{
|
||||||
|
scheduler: NewPriorityTaskScheduler(workers),
|
||||||
|
taskGroups: make(map[string]*TaskGroup),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateImageProcessingGroup 创建图片处理任务组
|
||||||
|
func (tm *TaskManager) CreateImageProcessingGroup(groupID string, imagePaths []string) error {
|
||||||
|
group := &TaskGroup{
|
||||||
|
ID: groupID,
|
||||||
|
Tasks: make([]Task, 0),
|
||||||
|
Status: "pending",
|
||||||
|
StartTime: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为每张图片创建压缩和EXIF读取任务
|
||||||
|
for i, path := range imagePaths {
|
||||||
|
// 压缩任务
|
||||||
|
compressTask := NewImageCompressTask(
|
||||||
|
fmt.Sprintf("%s_compress_%d", groupID, i),
|
||||||
|
path,
|
||||||
|
85,
|
||||||
|
)
|
||||||
|
group.Tasks = append(group.Tasks, compressTask)
|
||||||
|
|
||||||
|
// EXIF任务
|
||||||
|
exifTask := NewExifReadTask(
|
||||||
|
fmt.Sprintf("%s_exif_%d", groupID, i),
|
||||||
|
path,
|
||||||
|
)
|
||||||
|
group.Tasks = append(group.Tasks, exifTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.taskGroups[groupID] = group
|
||||||
|
|
||||||
|
// 提交所有任务
|
||||||
|
for _, task := range group.Tasks {
|
||||||
|
if err := tm.scheduler.SubmitWithPriority(task); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.Status = "running"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态Worker调整器
|
||||||
|
type DynamicWorkerManager struct {
|
||||||
|
scheduler *TaskScheduler
|
||||||
|
minWorkers int
|
||||||
|
maxWorkers int
|
||||||
|
checkInterval time.Duration
|
||||||
|
scaleThreshold float64 // 队列使用率阈值
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDynamicWorkerManager(scheduler *TaskScheduler, min, max int) *DynamicWorkerManager {
|
||||||
|
return &DynamicWorkerManager{
|
||||||
|
scheduler: scheduler,
|
||||||
|
minWorkers: min,
|
||||||
|
maxWorkers: max,
|
||||||
|
checkInterval: 5 * time.Second,
|
||||||
|
scaleThreshold: 0.8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dwm *DynamicWorkerManager) Start(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(dwm.checkInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
dwm.adjustWorkers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dwm *DynamicWorkerManager) adjustWorkers() {
|
||||||
|
stats := dwm.scheduler.GetStats()
|
||||||
|
pending := stats["pending_tasks"].(int)
|
||||||
|
//active := stats["active_workers"].(int)
|
||||||
|
|
||||||
|
// 计算队列使用率
|
||||||
|
queueUsage := float64(pending) / float64(cap(dwm.scheduler.taskQueue))
|
||||||
|
|
||||||
|
if queueUsage > dwm.scaleThreshold && dwm.scheduler.workers < dwm.maxWorkers {
|
||||||
|
// 增加worker
|
||||||
|
log.Printf("Scaling up workers: queue usage %.2f%%", queueUsage*100)
|
||||||
|
// 这里需要实现动态增加worker的逻辑
|
||||||
|
} else if queueUsage < 0.2 && dwm.scheduler.workers > dwm.minWorkers {
|
||||||
|
// 减少worker
|
||||||
|
log.Printf("Scaling down workers: queue usage %.2f%%", queueUsage*100)
|
||||||
|
// 这里需要实现动态减少worker的逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务监控器
|
||||||
|
type TaskMonitor struct {
|
||||||
|
scheduler *TaskScheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTaskMonitor(scheduler *TaskScheduler) *TaskMonitor {
|
||||||
|
return &TaskMonitor{scheduler: scheduler}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TaskMonitor) Start(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
tm.printStats()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *TaskMonitor) printStats() {
|
||||||
|
stats := tm.scheduler.GetStats()
|
||||||
|
|
||||||
|
fmt.Println("\n========== Task Scheduler Stats ==========")
|
||||||
|
fmt.Printf("Total Tasks: %d\n", stats["total_tasks"])
|
||||||
|
fmt.Printf("Completed: %d\n", stats["completed_tasks"])
|
||||||
|
fmt.Printf("Failed: %d\n", stats["failed_tasks"])
|
||||||
|
fmt.Printf("Pending: %d\n", stats["pending_tasks"])
|
||||||
|
fmt.Printf("Active Workers: %d\n", stats["active_workers"])
|
||||||
|
|
||||||
|
if typeStats, ok := stats["type_stats"].(map[string]*TaskTypeStats); ok {
|
||||||
|
fmt.Println("\nTask Type Statistics:")
|
||||||
|
for taskType, stat := range typeStats {
|
||||||
|
fmt.Printf(" %s: Total=%d, Completed=%d, Failed=%d, AvgTime=%v\n",
|
||||||
|
taskType, stat.Total, stat.Completed, stat.Failed, stat.AvgTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("==========================================\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主函数示例
|
||||||
|
func main() {
|
||||||
|
// 创建调度器 - 50个worker,队列容量10000
|
||||||
|
scheduler := NewTaskScheduler(50, 10000)
|
||||||
|
defer scheduler.Shutdown()
|
||||||
|
|
||||||
|
// 创建任务管理器
|
||||||
|
taskManager := NewTaskManager(50)
|
||||||
|
|
||||||
|
// 创建监控器
|
||||||
|
monitor := NewTaskMonitor(scheduler)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
go monitor.Start(ctx)
|
||||||
|
|
||||||
|
// 场景1:处理第一批10000张图片
|
||||||
|
log.Println("开始处理第一批图片...")
|
||||||
|
imagePaths1 := make([]string, 10000)
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
imagePaths1[i] = fmt.Sprintf("/batch1/image_%d.jpg", i)
|
||||||
|
}
|
||||||
|
taskManager.CreateImageProcessingGroup("batch1", imagePaths1)
|
||||||
|
|
||||||
|
// 模拟过一段时间后又来了新任务
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
// 场景2:又来了10000张图片
|
||||||
|
log.Println("开始处理第二批图片...")
|
||||||
|
imagePaths2 := make([]string, 10000)
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
imagePaths2[i] = fmt.Sprintf("/batch2/image_%d.jpg", i)
|
||||||
|
}
|
||||||
|
taskManager.CreateImageProcessingGroup("batch2", imagePaths2)
|
||||||
|
|
||||||
|
// 场景3:批量解析EXIF(优先级更高)
|
||||||
|
log.Println("开始批量解析EXIF...")
|
||||||
|
exifPaths := make([]string, 1000)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
exifPaths[i] = fmt.Sprintf("/priority/image_%d.jpg", i)
|
||||||
|
}
|
||||||
|
// 这些任务会有更高的优先级
|
||||||
|
for i, path := range exifPaths {
|
||||||
|
task := &HighPriorityExifTask{
|
||||||
|
ExifReadTask: *NewExifReadTask(fmt.Sprintf("priority_exif_%d", i), path),
|
||||||
|
}
|
||||||
|
taskManager.scheduler.SubmitWithPriority(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一段时间查看执行情况
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
|
// 打印最终统计
|
||||||
|
monitor.printStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HighPriorityExifTask 高优先级EXIF任务
|
||||||
|
type HighPriorityExifTask struct {
|
||||||
|
ExifReadTask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *HighPriorityExifTask) Priority() int {
|
||||||
|
return 10 // 更高的优先级
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务重试装饰器
|
||||||
|
type RetryableTask struct {
|
||||||
|
task Task
|
||||||
|
maxRetries int
|
||||||
|
retries int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRetryableTask(task Task, maxRetries int) *RetryableTask {
|
||||||
|
return &RetryableTask{
|
||||||
|
task: task,
|
||||||
|
maxRetries: maxRetries,
|
||||||
|
retries: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RetryableTask) Execute(ctx context.Context) error {
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
for rt.retries <= rt.maxRetries {
|
||||||
|
err := rt.task.Execute(ctx)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lastErr = err
|
||||||
|
rt.retries++
|
||||||
|
|
||||||
|
if rt.retries <= rt.maxRetries {
|
||||||
|
// 指数退避
|
||||||
|
backoff := time.Duration(rt.retries) * time.Second
|
||||||
|
log.Printf("Task %s failed, retrying in %v (attempt %d/%d)",
|
||||||
|
rt.task.ID(), backoff, rt.retries, rt.maxRetries)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-time.After(backoff):
|
||||||
|
// 继续重试
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("task failed after %d retries: %w", rt.maxRetries, lastErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RetryableTask) ID() string { return rt.task.ID() }
|
||||||
|
func (rt *RetryableTask) Type() string { return rt.task.Type() }
|
||||||
|
func (rt *RetryableTask) Priority() int { return rt.task.Priority() }
|
||||||
|
|
||||||
|
// 批处理优化 - 将多个小任务合并为一个大任务
|
||||||
|
type BatchImageCompressTask struct {
|
||||||
|
id string
|
||||||
|
filePaths []string
|
||||||
|
quality int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBatchImageCompressTask(id string, filePaths []string, quality int) *BatchImageCompressTask {
|
||||||
|
return &BatchImageCompressTask{
|
||||||
|
id: id,
|
||||||
|
filePaths: filePaths,
|
||||||
|
quality: quality,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BatchImageCompressTask) Execute(ctx context.Context) error {
|
||||||
|
// 批量处理多张图片,减少任务切换开销
|
||||||
|
for _, path := range t.filePaths {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
// 处理单张图片
|
||||||
|
log.Printf("Batch compressing: %s", path)
|
||||||
|
// 实际的压缩逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BatchImageCompressTask) ID() string { return t.id }
|
||||||
|
func (t *BatchImageCompressTask) Type() string { return "batch_image_compress" }
|
||||||
|
func (t *BatchImageCompressTask) Priority() int { return 5 }
|
||||||
+51
-1
@@ -10,7 +10,10 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"rear/internal/config"
|
||||||
|
"rear/internal/db"
|
||||||
"rear/internal/model"
|
"rear/internal/model"
|
||||||
|
"rear/internal/repositories"
|
||||||
"rear/internal/router"
|
"rear/internal/router"
|
||||||
"rear/internal/service"
|
"rear/internal/service"
|
||||||
"rear/pkg/logger"
|
"rear/pkg/logger"
|
||||||
@@ -27,6 +30,53 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 数据库初始化
|
// 数据库初始化
|
||||||
|
if err := db.InitDatabase(); err != nil {
|
||||||
|
log.Fatal("Failed to initialize database:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动迁移
|
||||||
|
if err := db.AutoMigrate(); err != nil {
|
||||||
|
log.Fatal("Failed to migrate database:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化基础服务(启动写操作处理协程)
|
||||||
|
repositories.InitBaseService()
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
userService := repositories.NewUserService()
|
||||||
|
|
||||||
|
// 创建用户
|
||||||
|
user := &model.User{
|
||||||
|
Email: "john@example.com",
|
||||||
|
Username: "John Doe",
|
||||||
|
Age: 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := userService.CreateUser(user); err != nil {
|
||||||
|
log.Printf("Failed to create user: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("User created successfully: %+v", user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户名称
|
||||||
|
if err := userService.UpdateUserName(user.ID, "John Smith"); err != nil {
|
||||||
|
log.Printf("Failed to update user name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户(可以并发执行)
|
||||||
|
if foundUser, err := userService.GetUserByID(user.ID); err != nil {
|
||||||
|
log.Printf("Failed to get user: %v", err)
|
||||||
|
} else if foundUser != nil {
|
||||||
|
log.Printf("Found user: %+v", foundUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有用户
|
||||||
|
if users, err := userService.GetAllUsers(); err != nil {
|
||||||
|
log.Printf("Failed to get all users: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Total users: %d", len(users))
|
||||||
|
}
|
||||||
|
|
||||||
//if err := initDatabase(); err != nil {
|
//if err := initDatabase(); err != nil {
|
||||||
// logger.Fatalf("Failed to initialize database: %v", err)
|
// logger.Fatalf("Failed to initialize database: %v", err)
|
||||||
//}
|
//}
|
||||||
@@ -38,7 +88,7 @@ func main() {
|
|||||||
|
|
||||||
func startHttp() {
|
func startHttp() {
|
||||||
// 配置加载
|
// 配置加载
|
||||||
netConfig := model.InitConfig()
|
netConfig := config.InitConfig()
|
||||||
// 设置Gin模式
|
// 设置Gin模式
|
||||||
gin.SetMode(netConfig.Mode)
|
gin.SetMode(netConfig.Mode)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user