feat: release v1.0.0
This commit is contained in:
@@ -48,13 +48,18 @@ func main() {
|
|||||||
fx.Provide(
|
fx.Provide(
|
||||||
config.NewConfig,
|
config.NewConfig,
|
||||||
logger.New,
|
logger.New,
|
||||||
|
|
||||||
|
db.NewDB,
|
||||||
|
db.NewStore,
|
||||||
|
db.NewRedisClient,
|
||||||
|
|
||||||
cache.NewCaches,
|
cache.NewCaches,
|
||||||
|
|
||||||
middleware.NewJWTMiddleware,
|
middleware.NewJWTMiddleware,
|
||||||
middleware.NewAuthMiddleware,
|
middleware.NewAuthMiddleware,
|
||||||
middleware.NewLoggerMiddleware,
|
middleware.NewLoggerMiddleware,
|
||||||
|
|
||||||
router.NewRouter,
|
router.NewRouter,
|
||||||
db.NewDB,
|
|
||||||
db.NewQueries,
|
|
||||||
),
|
),
|
||||||
handler.Module,
|
handler.Module,
|
||||||
service.Module,
|
service.Module,
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ database:
|
|||||||
dbname: db_name
|
dbname: db_name
|
||||||
sslMode: disable
|
sslMode: disable
|
||||||
|
|
||||||
|
redis:
|
||||||
|
addr: host:port
|
||||||
|
password: redis_password
|
||||||
|
db: 0
|
||||||
|
key_prefix: ""
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret:
|
secret:
|
||||||
signing_method: HS256
|
signing_method: HS256
|
||||||
@@ -19,6 +25,7 @@ jwt:
|
|||||||
audience:
|
audience:
|
||||||
token_header: Authorization
|
token_header: Authorization
|
||||||
token_prefix: "Bearer "
|
token_prefix: "Bearer "
|
||||||
|
cookie_name: "rt"
|
||||||
|
|
||||||
file:
|
file:
|
||||||
base_url: http://127.0.0.1:8080/static/ #静态文件目录 用于本地访问开放静态资源
|
base_url: http://127.0.0.1:8080/static/ #静态文件目录 用于本地访问开放静态资源
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -11,14 +11,15 @@ require (
|
|||||||
github.com/jackc/pgx/v5 v5.7.6
|
github.com/jackc/pgx/v5 v5.7.6
|
||||||
github.com/knadh/koanf v1.5.0
|
github.com/knadh/koanf v1.5.0
|
||||||
github.com/matoous/go-nanoid/v2 v2.1.0
|
github.com/matoous/go-nanoid/v2 v2.1.0
|
||||||
github.com/maypok86/otter/v2 v2.3.0
|
github.com/redis/go-redis/v9 v9.21.0
|
||||||
go.uber.org/fx v1.24.0
|
go.uber.org/fx v1.24.0
|
||||||
golang.org/x/crypto v0.46.0
|
golang.org/x/crypto v0.46.0
|
||||||
golang.org/x/sync v0.19.0
|
golang.org/x/sync v0.19.0
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
@@ -29,14 +30,13 @@ require (
|
|||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.10 // indirect
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
github.com/stretchr/testify v1.11.1 // indirect
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/dig v1.19.0 // indirect
|
go.uber.org/dig v1.19.0 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
go.uber.org/zap v1.26.0 // indirect
|
go.uber.org/zap v1.26.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
16
go.sum
16
go.sum
@@ -25,8 +25,14 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
|||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
@@ -162,6 +168,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
|
|||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
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/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
|
github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
|
||||||
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
|
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -188,8 +196,6 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
|
|||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/maypok86/otter/v2 v2.3.0 h1:8H8AVVFUSzJwIegKwv1uF5aGitTY+AIrtktg7OcLs8w=
|
|
||||||
github.com/maypok86/otter/v2 v2.3.0/go.mod h1:XgIdlpmL6jYz882/CAx1E4C1ukfgDKSaw4mWq59+7l8=
|
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
@@ -244,6 +250,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E=
|
||||||
|
github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||||
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
@@ -270,10 +278,14 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
|
|||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||||
|
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=
|
go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=
|
||||||
go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
||||||
go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg=
|
go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg=
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ type Database struct {
|
|||||||
SSLMode string `koanf:"sslMode"`
|
SSLMode string `koanf:"sslMode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Redis struct {
|
||||||
|
Addr string `koanf:"addr"`
|
||||||
|
Password string `koanf:"password"`
|
||||||
|
DB int `koanf:"db"`
|
||||||
|
KeyPrefix string `koanf:"key_prefix"`
|
||||||
|
}
|
||||||
|
|
||||||
type JWTConfig struct {
|
type JWTConfig struct {
|
||||||
Secret string `koanf:"secret"`
|
Secret string `koanf:"secret"`
|
||||||
SigningMethod string `koanf:"signing_method"`
|
SigningMethod string `koanf:"signing_method"`
|
||||||
@@ -41,6 +48,7 @@ type JWTConfig struct {
|
|||||||
Audience string `koanf:"audience"`
|
Audience string `koanf:"audience"`
|
||||||
TokenHeader string `koanf:"token_header"`
|
TokenHeader string `koanf:"token_header"`
|
||||||
TokenPrefix string `koanf:"token_prefix"`
|
TokenPrefix string `koanf:"token_prefix"`
|
||||||
|
CookieName string `koanf:"cookie_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
@@ -56,6 +64,7 @@ type LogConfig struct {
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Server Server `koanf:"server"`
|
Server Server `koanf:"server"`
|
||||||
Database Database `koanf:"database"`
|
Database Database `koanf:"database"`
|
||||||
|
Redis Redis `koanf:"redis"`
|
||||||
JWTConfig JWTConfig `koanf:"jwt"`
|
JWTConfig JWTConfig `koanf:"jwt"`
|
||||||
Log LogConfig `koanf:"log"`
|
Log LogConfig `koanf:"log"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"server/internal/config"
|
"server/internal/config"
|
||||||
"server/internal/db/sqlc"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
@@ -42,7 +41,3 @@ func NewDB(lc fx.Lifecycle, cfg *config.Config) (*pgxpool.Pool, error) {
|
|||||||
|
|
||||||
return pool, nil
|
return pool, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQueries(pool *pgxpool.Pool) *db.Queries {
|
|
||||||
return db.New(pool)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE sys_users
|
||||||
|
DROP COLUMN IF EXISTS status;
|
||||||
|
|
||||||
|
ALTER TABLE sys_roles
|
||||||
|
DROP COLUMN IF EXISTS status;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE sys_users
|
||||||
|
ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1;
|
||||||
|
|
||||||
|
ALTER TABLE sys_roles
|
||||||
|
ADD COLUMN IF NOT EXISTS status SMALLINT NOT NULL DEFAULT 1;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN sys_users.status IS '用户状态(0禁用,1启用)';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN sys_roles.status IS '角色(0禁用,1启用)';
|
||||||
6
internal/db/migrations/000009_add_post_tags.down.sql
Normal file
6
internal/db/migrations/000009_add_post_tags.down.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ALTER TABLE categories
|
||||||
|
DROP COLUMN IF EXISTS sort;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS tags;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS post_tag;
|
||||||
38
internal/db/migrations/000009_add_post_tags.up.sql
Normal file
38
internal/db/migrations/000009_add_post_tags.up.sql
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
-- 新增字段
|
||||||
|
ALTER TABLE categories
|
||||||
|
ADD COLUMN IF NOT EXISTS sort INTEGER DEFAULT 0;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN categories.sort IS '分类排序';
|
||||||
|
|
||||||
|
-- tag表
|
||||||
|
CREATE TABLE tags
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
sort INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TRIGGER update_tags_updated_at
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON tags
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tags.id IS '标签ID';
|
||||||
|
COMMENT ON COLUMN tags.name IS '标签名称';
|
||||||
|
COMMENT ON COLUMN tags.code IS '标签编码';
|
||||||
|
COMMENT ON COLUMN tags.sort IS '标签排序';
|
||||||
|
|
||||||
|
-- 关联表
|
||||||
|
CREATE TABLE post_tag
|
||||||
|
(
|
||||||
|
post_id INT NOT NULL,
|
||||||
|
tag_id INT NOT NULL,
|
||||||
|
PRIMARY KEY (post_id, tag_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN post_tag.post_id IS '文章ID';
|
||||||
|
COMMENT ON COLUMN post_tag.tag_id IS '标签id';
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
ALTER TABLE sys_menus
|
||||||
|
DROP CONSTRAINT IF EXISTS sys_menus_path_required_check;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS sys_menus_path_unique_idx;
|
||||||
|
|
||||||
|
ALTER TABLE sys_menus
|
||||||
|
ADD CONSTRAINT sys_menus_path_key UNIQUE (path);
|
||||||
|
|
||||||
|
ALTER TABLE sys_menus
|
||||||
|
ALTER COLUMN path SET NOT NULL;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
ALTER TABLE sys_menus
|
||||||
|
ALTER COLUMN path DROP NOT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE sys_menus
|
||||||
|
DROP CONSTRAINT IF EXISTS sys_menus_path_key;
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS sys_menus_path_unique_idx;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX sys_menus_path_unique_idx
|
||||||
|
ON sys_menus (path)
|
||||||
|
WHERE type IN (0, 1);
|
||||||
|
|
||||||
|
ALTER TABLE sys_menus
|
||||||
|
DROP CONSTRAINT IF EXISTS sys_menus_path_required_check;
|
||||||
|
|
||||||
|
ALTER TABLE sys_menus
|
||||||
|
ADD CONSTRAINT sys_menus_path_required_check
|
||||||
|
CHECK (type = 2 OR path IS NOT NULL);
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
-- name: CreateCategory :exec
|
-- name: CreateCategory :exec
|
||||||
INSERT INTO categories(name, code)
|
INSERT INTO categories(name, code, sort)
|
||||||
VALUES ($1, $2);
|
VALUES ($1, $2, $3);
|
||||||
|
|
||||||
-- name: ListCategories :many
|
-- name: ListCategories :many
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM categories
|
FROM categories
|
||||||
ORDER BY id LIMIT $1
|
ORDER BY sort DESC, id
|
||||||
OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
-- name: CountCategories :one
|
-- name: CountCategories :one
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
@@ -14,17 +14,14 @@ FROM categories;
|
|||||||
|
|
||||||
-- name: ListAllCategories :many
|
-- name: ListAllCategories :many
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM categories;
|
|
||||||
|
|
||||||
-- name: GetCategoryById :one
|
|
||||||
SELECT *
|
|
||||||
FROM categories
|
FROM categories
|
||||||
WHERE id = $1;
|
ORDER BY sort DESC, id;
|
||||||
|
|
||||||
-- name: UpdateCategory :execrows
|
-- name: UpdateCategory :execrows
|
||||||
UPDATE categories
|
UPDATE categories
|
||||||
SET name = coalesce(sqlc.narg('name'), name),
|
SET name = coalesce(sqlc.narg('name'), name),
|
||||||
code = coalesce(sqlc.narg('code'), code)
|
code = coalesce(sqlc.narg('code'), code),
|
||||||
|
sort = coalesce(sqlc.narg('sort'), sort)
|
||||||
WHERE id = sqlc.arg('id');
|
WHERE id = sqlc.arg('id');
|
||||||
|
|
||||||
-- name: DeleteCategory :execrows
|
-- name: DeleteCategory :execrows
|
||||||
@@ -36,7 +33,12 @@ WHERE id = $1;
|
|||||||
INSERT INTO post_category(post_id, category_id)
|
INSERT INTO post_category(post_id, category_id)
|
||||||
VALUES ($1, $2);
|
VALUES ($1, $2);
|
||||||
|
|
||||||
-- name: DeletePostCategory :exec
|
-- name: DeletePostCategoryByPostID :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM post_category
|
FROM post_category
|
||||||
WHERE post_id = $1;
|
WHERE post_id = $1;
|
||||||
|
|
||||||
|
-- name: DeletePostCategoryByCategoryID :exec
|
||||||
|
DELETE
|
||||||
|
FROM post_category
|
||||||
|
WHERE category_id = $1;
|
||||||
@@ -18,18 +18,46 @@ WITH paginated_posts AS (
|
|||||||
updated_at
|
updated_at
|
||||||
FROM posts
|
FROM posts
|
||||||
ORDER BY sort DESC, published_at DESC, id DESC
|
ORDER BY sort DESC, published_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2)
|
LIMIT $1 OFFSET $2
|
||||||
|
)
|
||||||
-- 第二步:用这极少量的记录去进行 JOIN
|
-- 第二步:用这极少量的记录去进行 JOIN
|
||||||
SELECT p.*,
|
SELECT p.*,
|
||||||
f.file_path AS cover,
|
f.file_path AS cover,
|
||||||
c.name AS category_name,
|
c.name AS category_name,
|
||||||
c.id AS category_id,
|
c.id AS category_id,
|
||||||
COALESCE(ps.view, 0) AS view
|
COALESCE(ps.view, 0) AS view,
|
||||||
|
COALESCE(
|
||||||
|
jsonb_agg(
|
||||||
|
jsonb_build_object(
|
||||||
|
'id', t.id,
|
||||||
|
'name', t.name,
|
||||||
|
'code', t.code
|
||||||
|
)
|
||||||
|
ORDER BY t.sort DESC, t.id
|
||||||
|
) FILTER (WHERE t.id IS NOT NULL),
|
||||||
|
'[]'::jsonb
|
||||||
|
) AS tags
|
||||||
FROM paginated_posts p
|
FROM paginated_posts p
|
||||||
LEFT JOIN files f ON f.id = p.cover_id
|
LEFT JOIN files f ON f.id = p.cover_id
|
||||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||||
LEFT JOIN categories c ON c.id = pc.category_id
|
LEFT JOIN categories c ON c.id = pc.category_id
|
||||||
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
||||||
|
LEFT JOIN post_tag pt ON pt.post_id = p.id
|
||||||
|
LEFT JOIN tags t ON t.id = pt.tag_id
|
||||||
|
GROUP BY p.id,
|
||||||
|
p.title,
|
||||||
|
p.cover_id,
|
||||||
|
p.slug,
|
||||||
|
p.summary,
|
||||||
|
p.status,
|
||||||
|
p.sort,
|
||||||
|
p.published_at,
|
||||||
|
p.created_at,
|
||||||
|
p.updated_at,
|
||||||
|
f.file_path,
|
||||||
|
c.name,
|
||||||
|
c.id,
|
||||||
|
ps.view
|
||||||
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC;
|
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC;
|
||||||
|
|
||||||
-- name: CountPosts :one
|
-- name: CountPosts :one
|
||||||
@@ -40,12 +68,21 @@ FROM posts;
|
|||||||
SELECT p.*,
|
SELECT p.*,
|
||||||
f.file_path AS cover,
|
f.file_path AS cover,
|
||||||
c.name AS category_name,
|
c.name AS category_name,
|
||||||
c.id AS category_id
|
c.id AS category_id,
|
||||||
|
COALESCE(
|
||||||
|
array_agg(pt.tag_id ORDER BY pt.tag_id) FILTER (WHERE pt.tag_id IS NOT NULL),
|
||||||
|
ARRAY[]::integer[]
|
||||||
|
) AS tags
|
||||||
FROM posts p
|
FROM posts p
|
||||||
LEFT JOIN files f ON f.id = p.cover_id
|
LEFT JOIN files f ON f.id = p.cover_id
|
||||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||||
LEFT JOIN categories c ON c.id = pc.category_id
|
LEFT JOIN categories c ON c.id = pc.category_id
|
||||||
|
LEFT JOIN post_tag pt ON pt.post_id = p.id
|
||||||
WHERE p.id = $1
|
WHERE p.id = $1
|
||||||
|
GROUP BY p.id,
|
||||||
|
f.file_path,
|
||||||
|
c.name,
|
||||||
|
c.id
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: UpdatePost :execrows
|
-- name: UpdatePost :execrows
|
||||||
@@ -98,12 +135,36 @@ SELECT p.*,
|
|||||||
f.file_path AS cover,
|
f.file_path AS cover,
|
||||||
c.name AS category_name,
|
c.name AS category_name,
|
||||||
c.id AS category_id,
|
c.id AS category_id,
|
||||||
COALESCE(ps.view, 0) AS view
|
COALESCE(ps.view, 0) AS view,
|
||||||
|
COALESCE(
|
||||||
|
jsonb_agg(
|
||||||
|
jsonb_build_object(
|
||||||
|
'id', t.id,
|
||||||
|
'name', t.name,
|
||||||
|
'code', t.code
|
||||||
|
)
|
||||||
|
ORDER BY t.sort DESC, t.id
|
||||||
|
) FILTER (WHERE t.id IS NOT NULL),
|
||||||
|
'[]'::jsonb
|
||||||
|
) AS tags
|
||||||
FROM paginated_posts p
|
FROM paginated_posts p
|
||||||
LEFT JOIN files f ON f.id = p.cover_id
|
LEFT JOIN files f ON f.id = p.cover_id
|
||||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||||
LEFT JOIN categories c ON c.id = pc.category_id
|
LEFT JOIN categories c ON c.id = pc.category_id
|
||||||
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
||||||
|
LEFT JOIN post_tag pt ON pt.post_id = p.id
|
||||||
|
LEFT JOIN tags t ON t.id = pt.tag_id
|
||||||
|
GROUP BY p.id,
|
||||||
|
p.title,
|
||||||
|
p.cover_id,
|
||||||
|
p.slug,
|
||||||
|
p.summary,
|
||||||
|
p.sort,
|
||||||
|
p.published_at,
|
||||||
|
f.file_path,
|
||||||
|
c.name,
|
||||||
|
c.id,
|
||||||
|
ps.view
|
||||||
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC;
|
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC;
|
||||||
|
|
||||||
-- name: CountPublishedPosts :one
|
-- name: CountPublishedPosts :one
|
||||||
@@ -142,4 +203,6 @@ FROM
|
|||||||
AND p.published_at < NOW()
|
AND p.published_at < NOW()
|
||||||
GROUP BY
|
GROUP BY
|
||||||
c.id,
|
c.id,
|
||||||
c.name;
|
c.sort,
|
||||||
|
c.name
|
||||||
|
ORDER BY c.sort DESC, c.id;
|
||||||
@@ -6,12 +6,16 @@ RETURNING id;
|
|||||||
-- name: GetSysApis :many
|
-- name: GetSysApis :many
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM sys_apis
|
FROM sys_apis
|
||||||
|
WHERE (sqlc.arg('group_name')::text = '' OR group_name ILIKE '%' || sqlc.arg('group_name') || '%')
|
||||||
|
AND (sqlc.arg('method')::text = '' OR method = sqlc.arg('method'))
|
||||||
ORDER BY id
|
ORDER BY id
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
-- name: CountSysApis :one
|
-- name: CountSysApis :one
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM sys_apis;
|
FROM sys_apis
|
||||||
|
WHERE (sqlc.arg('group_name')::text = '' OR group_name ILIKE '%' || sqlc.arg('group_name') || '%')
|
||||||
|
AND (sqlc.arg('method')::text = '' OR method = sqlc.arg('method'));
|
||||||
|
|
||||||
-- name: GetSysApiByID :one
|
-- name: GetSysApiByID :one
|
||||||
SELECT *
|
SELECT *
|
||||||
@@ -41,13 +45,6 @@ DELETE
|
|||||||
FROM sys_api_permission
|
FROM sys_api_permission
|
||||||
WHERE api_id = $1;
|
WHERE api_id = $1;
|
||||||
|
|
||||||
-- name: DeleteSysPermissionBySysApiID :exec
|
|
||||||
DELETE
|
|
||||||
FROM sys_permissions
|
|
||||||
WHERE id IN (SELECT permission_id
|
|
||||||
FROM sys_api_permission
|
|
||||||
WHERE api_id = $1);
|
|
||||||
|
|
||||||
-- name: GetSysApiGroupNames :many
|
-- name: GetSysApiGroupNames :many
|
||||||
SELECT DISTINCT group_name
|
SELECT DISTINCT group_name
|
||||||
FROM sys_apis;
|
FROM sys_apis;
|
||||||
@@ -38,7 +38,7 @@ SET name = coalesce(sqlc.narg('name'), name),
|
|||||||
hidden = coalesce(sqlc.narg('hidden'), hidden),
|
hidden = coalesce(sqlc.narg('hidden'), hidden),
|
||||||
sort = coalesce(sqlc.narg('sort'), sort),
|
sort = coalesce(sqlc.narg('sort'), sort),
|
||||||
status = coalesce(sqlc.narg('status'), status),
|
status = coalesce(sqlc.narg('status'), status),
|
||||||
icon = coalesce(sqlc.narg('icon'), status),
|
icon = CASE WHEN @update_icon::boolean THEN @icon ELSE icon END,
|
||||||
parent_id = CASE WHEN @update_parent_id::boolean THEN @parent_id ELSE parent_id END
|
parent_id = CASE WHEN @update_parent_id::boolean THEN @parent_id ELSE parent_id END
|
||||||
WHERE id = sqlc.arg('id');
|
WHERE id = sqlc.arg('id');
|
||||||
|
|
||||||
@@ -12,3 +12,12 @@ DELETE
|
|||||||
FROM sys_permissions
|
FROM sys_permissions
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: GetSysPermissionIDBySysApiID :one
|
||||||
|
SELECT permission_id
|
||||||
|
FROM sys_api_permission
|
||||||
|
WHERE api_id = $1;
|
||||||
|
|
||||||
|
-- name: GetSysPermissionIDBySysMenuID :one
|
||||||
|
SELECT permission_id
|
||||||
|
FROM sys_menu_permission
|
||||||
|
WHERE menu_id = $1;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
-- name: CreateSysRole :exec
|
-- name: CreateSysRole :exec
|
||||||
INSERT INTO sys_roles (name, code)
|
INSERT INTO sys_roles (name, code, status)
|
||||||
VALUES ($1, $2);
|
VALUES ($1, $2, $3);
|
||||||
|
|
||||||
-- name: ListSysRoles :many
|
-- name: ListSysRoles :many
|
||||||
SELECT *
|
SELECT *
|
||||||
@@ -28,7 +28,8 @@ SELECT EXISTS(SELECT 1
|
|||||||
|
|
||||||
-- name: UpdateSysRole :execrows
|
-- name: UpdateSysRole :execrows
|
||||||
UPDATE sys_roles
|
UPDATE sys_roles
|
||||||
SET name = coalesce(sqlc.narg('name'), name)
|
SET name = coalesce(sqlc.narg('name'), name),
|
||||||
|
status = coalesce(sqlc.narg('status'), status)
|
||||||
WHERE id = sqlc.arg('id');
|
WHERE id = sqlc.arg('id');
|
||||||
|
|
||||||
-- name: DeleteSysRole :execrows
|
-- name: DeleteSysRole :execrows
|
||||||
@@ -68,9 +69,24 @@ FROM sys_role_permission rp
|
|||||||
JOIN sys_apis a ON a.id = ap.api_id
|
JOIN sys_apis a ON a.id = ap.api_id
|
||||||
WHERE rp.role_id = $1;
|
WHERE rp.role_id = $1;
|
||||||
|
|
||||||
-- name: DeleteSysRolePermission :exec
|
-- name: DeleteSysRolePermissionByType :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM sys_role_permission rp USING sys_permissions p
|
FROM sys_role_permission rp USING sys_permissions p
|
||||||
WHERE rp.permission_id = p.id
|
WHERE rp.permission_id = p.id
|
||||||
AND p."type" = $2
|
AND rp.role_id = $1
|
||||||
AND rp.role_id = $1;
|
AND p."type" = $2;
|
||||||
|
|
||||||
|
-- name: DeleteSysRolePermissionByRoleID :exec
|
||||||
|
DELETE
|
||||||
|
FROM sys_role_permission
|
||||||
|
WHERE role_id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteSysRolePermissionByPermissionID :exec
|
||||||
|
DELETE
|
||||||
|
FROM sys_role_permission
|
||||||
|
WHERE permission_id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteSysUserRoleByRoleID :exec
|
||||||
|
DELETE
|
||||||
|
FROM sys_user_role
|
||||||
|
WHERE role_id = $1;
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
-- name: CreateSysUser :exec
|
-- name: CreateSysUser :exec
|
||||||
INSERT INTO sys_users (account, username, password_hash, avatar_id)
|
INSERT INTO sys_users (account, username, password_hash, status, avatar_id)
|
||||||
VALUES ($1, $2, $3, $4);
|
VALUES ($1, $2, $3, $4, $5);
|
||||||
|
|
||||||
|
-- name: GetActiveSysUserByID :one
|
||||||
|
-- 场景:用户登录、获取个人信息、刷新 Token(严格校验 status = 1)
|
||||||
|
SELECT id, account, username, status
|
||||||
|
FROM sys_users
|
||||||
|
WHERE id = $1
|
||||||
|
AND status = 1;
|
||||||
|
|
||||||
-- name: GetSysUserByID :one
|
-- name: GetSysUserByID :one
|
||||||
SELECT u.id,
|
SELECT u.id,
|
||||||
u.account,
|
u.account,
|
||||||
u.username,
|
u.username,
|
||||||
u.avatar_id,
|
u.avatar_id,
|
||||||
|
u.status,
|
||||||
f.file_path AS avatar_url,
|
f.file_path AS avatar_url,
|
||||||
u.created_at,
|
u.created_at,
|
||||||
u.updated_at
|
u.updated_at
|
||||||
@@ -19,6 +27,7 @@ SELECT u.id,
|
|||||||
u.account,
|
u.account,
|
||||||
u.username,
|
u.username,
|
||||||
u.password_hash,
|
u.password_hash,
|
||||||
|
u.status,
|
||||||
u.avatar_id,
|
u.avatar_id,
|
||||||
f.file_path AS avatar_url,
|
f.file_path AS avatar_url,
|
||||||
u.created_at,
|
u.created_at,
|
||||||
@@ -31,22 +40,26 @@ WHERE u.account = $1;
|
|||||||
SELECT u.id,
|
SELECT u.id,
|
||||||
u.account,
|
u.account,
|
||||||
u.username,
|
u.username,
|
||||||
|
u.status,
|
||||||
u.avatar_id,
|
u.avatar_id,
|
||||||
f.file_path AS avatar_url,
|
f.file_path AS avatar_url,
|
||||||
u.created_at,
|
u.created_at,
|
||||||
u.updated_at
|
u.updated_at
|
||||||
FROM sys_users u
|
FROM sys_users u
|
||||||
LEFT JOIN files f ON u.avatar_id = f.id
|
LEFT JOIN files f ON u.avatar_id = f.id
|
||||||
|
WHERE (sqlc.arg('username')::text = '' OR u.username ILIKE '%' || sqlc.arg('username') || '%')
|
||||||
ORDER BY u.id
|
ORDER BY u.id
|
||||||
LIMIT $1 OFFSET $2;
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
-- name: CountSysUsers :one
|
-- name: CountSysUsers :one
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM sys_users;
|
FROM sys_users
|
||||||
|
WHERE (sqlc.arg('username')::text = '' OR username ILIKE '%' || sqlc.arg('username') || '%');
|
||||||
|
|
||||||
-- name: UpdateSysUser :execrows
|
-- name: UpdateSysUser :execrows
|
||||||
UPDATE sys_users
|
UPDATE sys_users
|
||||||
SET username = coalesce(sqlc.narg('username'), username),
|
SET username = coalesce(sqlc.narg('username'), username),
|
||||||
|
status = coalesce(sqlc.narg('status'), status),
|
||||||
avatar_id = CASE WHEN @update_avatar_id::boolean THEN @avatar_id ELSE avatar_id END
|
avatar_id = CASE WHEN @update_avatar_id::boolean THEN @avatar_id ELSE avatar_id END
|
||||||
WHERE id = sqlc.arg('id');
|
WHERE id = sqlc.arg('id');
|
||||||
|
|
||||||
@@ -66,7 +79,7 @@ DELETE
|
|||||||
FROM sys_user_role
|
FROM sys_user_role
|
||||||
WHERE user_id = $1;
|
WHERE user_id = $1;
|
||||||
|
|
||||||
-- name: CreateSysUserRole :exec
|
-- name: CreateSysUserRole :copyfrom
|
||||||
INSERT INTO sys_user_role (user_id, role_id)
|
INSERT INTO sys_user_role (user_id, role_id)
|
||||||
VALUES ($1, $2);
|
VALUES ($1, $2);
|
||||||
|
|
||||||
@@ -79,19 +92,44 @@ WHERE ur.user_id = $1;
|
|||||||
-- name: GetSysAdminMenus :many
|
-- name: GetSysAdminMenus :many
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM sys_menus
|
FROM sys_menus
|
||||||
|
WHERE status = 1
|
||||||
|
AND type <> 2
|
||||||
ORDER BY sort, id;
|
ORDER BY sort, id;
|
||||||
|
|
||||||
|
-- name: GetSysAdminPermissions :many
|
||||||
|
SELECT code
|
||||||
|
FROM sys_permissions
|
||||||
|
WHERE type = 0;
|
||||||
|
|
||||||
-- name: GetSysUserMenus :many
|
-- name: GetSysUserMenus :many
|
||||||
SELECT DISTINCT m.*
|
SELECT DISTINCT m.*
|
||||||
FROM sys_users u
|
FROM sys_users u
|
||||||
JOIN sys_user_role ur ON ur.user_id = u.id
|
JOIN sys_user_role ur ON ur.user_id = u.id
|
||||||
JOIN sys_roles r ON r.id = ur.role_id
|
JOIN sys_roles r ON r.id = ur.role_id
|
||||||
JOIN sys_role_permission rp ON rp.role_id = r.id
|
JOIN sys_role_permission rp ON rp.role_id = r.id
|
||||||
|
JOIN sys_menu_permission mp ON mp.permission_id = rp.permission_id
|
||||||
|
JOIN sys_menus m ON m.id = mp.menu_id
|
||||||
|
WHERE u.id = $1
|
||||||
|
AND u.status = 1
|
||||||
|
AND r.status = 1
|
||||||
|
AND m.status = 1
|
||||||
|
AND m.type <> 2
|
||||||
|
ORDER BY m.sort, m.id;
|
||||||
|
|
||||||
|
-- name: GetSysUserPermissions :many
|
||||||
|
SELECT DISTINCT p.code
|
||||||
|
FROM sys_users u
|
||||||
|
JOIN sys_user_role ur ON u.id = ur.user_id
|
||||||
|
JOIN sys_roles r on r.id = ur.role_id
|
||||||
|
JOIN sys_role_permission rp ON rp.role_id = r.id
|
||||||
JOIN sys_permissions p ON p.id = rp.permission_id
|
JOIN sys_permissions p ON p.id = rp.permission_id
|
||||||
JOIN sys_menu_permission mp ON mp.permission_id = p.id
|
JOIN sys_menu_permission mp ON mp.permission_id = p.id
|
||||||
JOIN sys_menus m ON m.id = mp.menu_id
|
JOIN sys_menus m ON m.id = mp.menu_id
|
||||||
WHERE u.id = $1
|
WHERE u.id = $1
|
||||||
ORDER BY m.sort, m.id;
|
AND u.status = 1
|
||||||
|
AND r.status = 1
|
||||||
|
AND m.status = 1
|
||||||
|
AND p."type" = 0;
|
||||||
|
|
||||||
-- name: GetSysUserApis :many
|
-- name: GetSysUserApis :many
|
||||||
SELECT DISTINCT a.id,
|
SELECT DISTINCT a.id,
|
||||||
@@ -99,9 +137,10 @@ SELECT DISTINCT a.id,
|
|||||||
a.METHOD
|
a.METHOD
|
||||||
FROM sys_users u
|
FROM sys_users u
|
||||||
JOIN sys_user_role ur ON u.id = ur.user_id
|
JOIN sys_user_role ur ON u.id = ur.user_id
|
||||||
JOIN sys_role_permission rp ON rp.role_id = ur.role_id
|
JOIN sys_roles r ON r.id = ur.role_id
|
||||||
JOIN sys_permissions p ON p.id = rp.permission_id
|
JOIN sys_role_permission rp ON rp.role_id = r.id
|
||||||
AND p.TYPE = 1
|
JOIN sys_api_permission ap ON ap.permission_id = rp.permission_id
|
||||||
JOIN sys_api_permission ap ON ap.permission_id = p.id
|
|
||||||
JOIN sys_apis a ON a.id = ap.api_id
|
JOIN sys_apis a ON a.id = ap.api_id
|
||||||
WHERE u.id = $1;
|
WHERE u.id = $1
|
||||||
|
AND u.status = 1
|
||||||
|
AND r.status = 1;
|
||||||
44
internal/db/query/tags.sql
Normal file
44
internal/db/query/tags.sql
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
-- name: ListTags :many
|
||||||
|
SELECT *
|
||||||
|
FROM tags
|
||||||
|
ORDER BY sort DESC, id
|
||||||
|
LIMIT $1 OFFSET $2;
|
||||||
|
|
||||||
|
-- name: CountTags :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM tags;
|
||||||
|
|
||||||
|
-- name: ListAllTags :many
|
||||||
|
SELECT *
|
||||||
|
FROM tags
|
||||||
|
ORDER BY sort DESC, id;
|
||||||
|
|
||||||
|
-- name: CreateTag :exec
|
||||||
|
INSERT INTO tags(name, code, sort)
|
||||||
|
VALUES ($1, $2, $3);
|
||||||
|
|
||||||
|
-- name: UpdateTag :execrows
|
||||||
|
UPDATE tags
|
||||||
|
SET name = coalesce(sqlc.narg('name'), name),
|
||||||
|
code = coalesce(sqlc.narg('code'), code),
|
||||||
|
sort = coalesce(sqlc.narg('sort'), sort)
|
||||||
|
WHERE id = sqlc.arg('id');
|
||||||
|
|
||||||
|
-- name: DeleteTag :execrows
|
||||||
|
DELETE
|
||||||
|
FROM tags
|
||||||
|
WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: CreatePostTag :copyfrom
|
||||||
|
INSERT INTO post_tag(post_id, tag_id)
|
||||||
|
values ($1, $2);
|
||||||
|
|
||||||
|
-- name: DeletePostTagByPostID :exec
|
||||||
|
DELETE
|
||||||
|
FROM post_tag
|
||||||
|
WHERE post_id = $1;
|
||||||
|
|
||||||
|
-- name: DeletePostTagByTagID :exec
|
||||||
|
DELETE
|
||||||
|
FROM post_tag
|
||||||
|
WHERE tag_id = $1;
|
||||||
27
internal/db/redis.go
Normal file
27
internal/db/redis.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
|
"server/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRedisClient(cfg *config.Config) (*redis.Client, error) {
|
||||||
|
client := redis.NewClient(&redis.Options{
|
||||||
|
Addr: cfg.Redis.Addr,
|
||||||
|
Password: cfg.Redis.Password,
|
||||||
|
DB: cfg.Redis.DB,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := client.Ping(context.Background()).Err(); err != nil {
|
||||||
|
slog.Error("failed to connect redis", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("redis connected", "addr", cfg.Redis.Addr)
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ CREATE TABLE categories
|
|||||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
code TEXT UNIQUE NOT NULL,
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
sort INTEGER DEFAULT 0,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ
|
updated_at TIMESTAMPTZ
|
||||||
);
|
);
|
||||||
@@ -17,3 +18,4 @@ EXECUTE FUNCTION update_updated_at_column();
|
|||||||
COMMENT ON COLUMN categories.id IS '分类ID';
|
COMMENT ON COLUMN categories.id IS '分类ID';
|
||||||
COMMENT ON COLUMN categories.name IS '分类名称';
|
COMMENT ON COLUMN categories.name IS '分类名称';
|
||||||
COMMENT ON COLUMN categories.code IS '分类编码';
|
COMMENT ON COLUMN categories.code IS '分类编码';
|
||||||
|
COMMENT ON COLUMN categories.sort IS '分类排序';
|
||||||
9
internal/db/schema/post_tag.sql
Normal file
9
internal/db/schema/post_tag.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE post_tag
|
||||||
|
(
|
||||||
|
post_id INT NOT NULL,
|
||||||
|
tag_id INT NOT NULL,
|
||||||
|
PRIMARY KEY (post_id, tag_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON COLUMN post_tag.post_id IS '文章ID';
|
||||||
|
COMMENT ON COLUMN post_tag.tag_id IS '标签id';
|
||||||
@@ -2,7 +2,7 @@ CREATE TABLE sys_menus
|
|||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
path TEXT NOT NULL,
|
path TEXT,
|
||||||
component TEXT,
|
component TEXT,
|
||||||
type SMALLINT NOT NULL,
|
type SMALLINT NOT NULL,
|
||||||
hidden BOOLEAN DEFAULT FALSE,
|
hidden BOOLEAN DEFAULT FALSE,
|
||||||
@@ -17,6 +17,9 @@ CREATE TABLE sys_menus
|
|||||||
CHECK (status IN (0, 1))
|
CHECK (status IN (0, 1))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX sys_menus_path_unique_idx
|
||||||
|
ON sys_menus (path)
|
||||||
|
WHERE type IN (0, 1);
|
||||||
|
|
||||||
CREATE TRIGGER update_sys_menus_updated_at
|
CREATE TRIGGER update_sys_menus_updated_at
|
||||||
BEFORE UPDATE
|
BEFORE UPDATE
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ CREATE TABLE sys_permissions
|
|||||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
code TEXT UNIQUE,
|
code TEXT UNIQUE,
|
||||||
type SMALLINT NOT NULL,
|
type SMALLINT NOT NULL,
|
||||||
created_at TIMESTAMPTZtz NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ,
|
updated_at TIMESTAMPTZ,
|
||||||
|
|
||||||
CONSTRAINT unique_type_code UNIQUE (type, code)
|
CONSTRAINT unique_type_code UNIQUE (type, code)
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ CREATE TABLE sys_roles
|
|||||||
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
code TEXT NOT NULL UNIQUE,
|
code TEXT NOT NULL UNIQUE,
|
||||||
|
status SMALLINT NOT NULL DEFAULT 1,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ
|
updated_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
CHECK (status IN (0, 1))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER update_sys_roles_updated_at
|
CREATE TRIGGER update_sys_roles_updated_at
|
||||||
@@ -16,5 +19,6 @@ EXECUTE FUNCTION update_updated_at_column();
|
|||||||
COMMENT ON COLUMN sys_roles.id IS '主键ID';
|
COMMENT ON COLUMN sys_roles.id IS '主键ID';
|
||||||
COMMENT ON COLUMN sys_roles.name IS '角色名称';
|
COMMENT ON COLUMN sys_roles.name IS '角色名称';
|
||||||
COMMENT ON COLUMN sys_roles.code IS '角色编码,唯一';
|
COMMENT ON COLUMN sys_roles.code IS '角色编码,唯一';
|
||||||
|
COMMENT ON COLUMN sys_roles.status IS '角色状态 0:禁用 1:启用';
|
||||||
COMMENT ON COLUMN sys_roles.created_at IS '创建时间';
|
COMMENT ON COLUMN sys_roles.created_at IS '创建时间';
|
||||||
COMMENT ON COLUMN sys_roles.updated_at IS '更新时间';
|
COMMENT ON COLUMN sys_roles.updated_at IS '更新时间';
|
||||||
@@ -4,9 +4,12 @@ CREATE TABLE sys_users
|
|||||||
account TEXT NOT NULL UNIQUE,
|
account TEXT NOT NULL UNIQUE,
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
password_hash TEXT NOT NULL,
|
password_hash TEXT NOT NULL,
|
||||||
|
status SMALLINT NOT NULL DEFAULT 1,
|
||||||
avatar_id INTEGER,
|
avatar_id INTEGER,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ
|
updated_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
CHECK (status IN (0, 1))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_sys_users_account ON sys_users (account);
|
CREATE INDEX idx_sys_users_account ON sys_users (account);
|
||||||
@@ -22,6 +25,7 @@ COMMENT ON COLUMN sys_users.id IS '主键ID';
|
|||||||
COMMENT ON COLUMN sys_users.account IS '用户账号';
|
COMMENT ON COLUMN sys_users.account IS '用户账号';
|
||||||
COMMENT ON COLUMN sys_users.username IS '用户名称';
|
COMMENT ON COLUMN sys_users.username IS '用户名称';
|
||||||
COMMENT ON COLUMN sys_users.password_hash IS 'hash密码';
|
COMMENT ON COLUMN sys_users.password_hash IS 'hash密码';
|
||||||
|
COMMENT ON COLUMN sys_users.status IS '用户状态 0:禁用 1:启用';
|
||||||
COMMENT ON COLUMN sys_users.avatar_id IS '头像文件id';
|
COMMENT ON COLUMN sys_users.avatar_id IS '头像文件id';
|
||||||
COMMENT ON COLUMN sys_users.created_at IS '创建时间';
|
COMMENT ON COLUMN sys_users.created_at IS '创建时间';
|
||||||
COMMENT ON COLUMN sys_users.updated_at IS '更新时间';
|
COMMENT ON COLUMN sys_users.updated_at IS '更新时间';
|
||||||
20
internal/db/schema/tags.sql
Normal file
20
internal/db/schema/tags.sql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
CREATE TABLE tags
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
sort INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TRIGGER update_tags_updated_at
|
||||||
|
BEFORE UPDATE
|
||||||
|
ON tags
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_updated_at_column();
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tags.id IS '标签ID';
|
||||||
|
COMMENT ON COLUMN tags.name IS '标签名称';
|
||||||
|
COMMENT ON COLUMN tags.code IS '标签编码';
|
||||||
|
COMMENT ON COLUMN tags.sort IS '标签排序';
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: category.sql
|
// source: categories.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -22,17 +22,18 @@ func (q *Queries) CountCategories(ctx context.Context) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createCategory = `-- name: CreateCategory :exec
|
const createCategory = `-- name: CreateCategory :exec
|
||||||
INSERT INTO categories(name, code)
|
INSERT INTO categories(name, code, sort)
|
||||||
VALUES ($1, $2)
|
VALUES ($1, $2, $3)
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateCategoryParams struct {
|
type CreateCategoryParams struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateCategory(ctx context.Context, arg CreateCategoryParams) error {
|
func (q *Queries) CreateCategory(ctx context.Context, arg CreateCategoryParams) error {
|
||||||
_, err := q.db.Exec(ctx, createCategory, arg.Name, arg.Code)
|
_, err := q.db.Exec(ctx, createCategory, arg.Name, arg.Code, arg.Sort)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,39 +66,32 @@ func (q *Queries) DeleteCategory(ctx context.Context, id int32) (int64, error) {
|
|||||||
return result.RowsAffected(), nil
|
return result.RowsAffected(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletePostCategory = `-- name: DeletePostCategory :exec
|
const deletePostCategoryByCategoryID = `-- name: DeletePostCategoryByCategoryID :exec
|
||||||
|
DELETE
|
||||||
|
FROM post_category
|
||||||
|
WHERE category_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeletePostCategoryByCategoryID(ctx context.Context, categoryID int32) error {
|
||||||
|
_, err := q.db.Exec(ctx, deletePostCategoryByCategoryID, categoryID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletePostCategoryByPostID = `-- name: DeletePostCategoryByPostID :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM post_category
|
FROM post_category
|
||||||
WHERE post_id = $1
|
WHERE post_id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) DeletePostCategory(ctx context.Context, postID int32) error {
|
func (q *Queries) DeletePostCategoryByPostID(ctx context.Context, postID int32) error {
|
||||||
_, err := q.db.Exec(ctx, deletePostCategory, postID)
|
_, err := q.db.Exec(ctx, deletePostCategoryByPostID, postID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCategoryById = `-- name: GetCategoryById :one
|
|
||||||
SELECT id, name, code, created_at, updated_at
|
|
||||||
FROM categories
|
|
||||||
WHERE id = $1
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *Queries) GetCategoryById(ctx context.Context, id int32) (Category, error) {
|
|
||||||
row := q.db.QueryRow(ctx, getCategoryById, id)
|
|
||||||
var i Category
|
|
||||||
err := row.Scan(
|
|
||||||
&i.ID,
|
|
||||||
&i.Name,
|
|
||||||
&i.Code,
|
|
||||||
&i.CreatedAt,
|
|
||||||
&i.UpdatedAt,
|
|
||||||
)
|
|
||||||
return i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
const listAllCategories = `-- name: ListAllCategories :many
|
const listAllCategories = `-- name: ListAllCategories :many
|
||||||
SELECT id, name, code, created_at, updated_at
|
SELECT id, name, code, sort, created_at, updated_at
|
||||||
FROM categories
|
FROM categories
|
||||||
|
ORDER BY sort DESC, id
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) ListAllCategories(ctx context.Context) ([]Category, error) {
|
func (q *Queries) ListAllCategories(ctx context.Context) ([]Category, error) {
|
||||||
@@ -113,6 +107,7 @@ func (q *Queries) ListAllCategories(ctx context.Context) ([]Category, error) {
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Code,
|
&i.Code,
|
||||||
|
&i.Sort,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -127,10 +122,10 @@ func (q *Queries) ListAllCategories(ctx context.Context) ([]Category, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listCategories = `-- name: ListCategories :many
|
const listCategories = `-- name: ListCategories :many
|
||||||
SELECT id, name, code, created_at, updated_at
|
SELECT id, name, code, sort, created_at, updated_at
|
||||||
FROM categories
|
FROM categories
|
||||||
ORDER BY id LIMIT $1
|
ORDER BY sort DESC, id
|
||||||
OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListCategoriesParams struct {
|
type ListCategoriesParams struct {
|
||||||
@@ -151,6 +146,7 @@ func (q *Queries) ListCategories(ctx context.Context, arg ListCategoriesParams)
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Code,
|
&i.Code,
|
||||||
|
&i.Sort,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -167,18 +163,25 @@ func (q *Queries) ListCategories(ctx context.Context, arg ListCategoriesParams)
|
|||||||
const updateCategory = `-- name: UpdateCategory :execrows
|
const updateCategory = `-- name: UpdateCategory :execrows
|
||||||
UPDATE categories
|
UPDATE categories
|
||||||
SET name = coalesce($1, name),
|
SET name = coalesce($1, name),
|
||||||
code = coalesce($2, code)
|
code = coalesce($2, code),
|
||||||
WHERE id = $3
|
sort = coalesce($3, sort)
|
||||||
|
WHERE id = $4
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateCategoryParams struct {
|
type UpdateCategoryParams struct {
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Code *string `json:"code"`
|
Code *string `json:"code"`
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdateCategory(ctx context.Context, arg UpdateCategoryParams) (int64, error) {
|
func (q *Queries) UpdateCategory(ctx context.Context, arg UpdateCategoryParams) (int64, error) {
|
||||||
result, err := q.db.Exec(ctx, updateCategory, arg.Name, arg.Code, arg.ID)
|
result, err := q.db.Exec(ctx, updateCategory,
|
||||||
|
arg.Name,
|
||||||
|
arg.Code,
|
||||||
|
arg.Sort,
|
||||||
|
arg.ID,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,45 @@
|
|||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: copyfrom.go
|
// source: copyfrom.go
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// iteratorForCreatePostTag implements pgx.CopyFromSource.
|
||||||
|
type iteratorForCreatePostTag struct {
|
||||||
|
rows []CreatePostTagParams
|
||||||
|
skippedFirstNextCall bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *iteratorForCreatePostTag) Next() bool {
|
||||||
|
if len(r.rows) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !r.skippedFirstNextCall {
|
||||||
|
r.skippedFirstNextCall = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
r.rows = r.rows[1:]
|
||||||
|
return len(r.rows) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r iteratorForCreatePostTag) Values() ([]interface{}, error) {
|
||||||
|
return []interface{}{
|
||||||
|
r.rows[0].PostID,
|
||||||
|
r.rows[0].TagID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r iteratorForCreatePostTag) Err() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreatePostTag(ctx context.Context, arg []CreatePostTagParams) (int64, error) {
|
||||||
|
return q.db.CopyFrom(ctx, []string{"post_tag"}, []string{"post_id", "tag_id"}, &iteratorForCreatePostTag{rows: arg})
|
||||||
|
}
|
||||||
|
|
||||||
// iteratorForCreateSysRolePermission implements pgx.CopyFromSource.
|
// iteratorForCreateSysRolePermission implements pgx.CopyFromSource.
|
||||||
type iteratorForCreateSysRolePermission struct {
|
type iteratorForCreateSysRolePermission struct {
|
||||||
rows []CreateSysRolePermissionParams
|
rows []CreateSysRolePermissionParams
|
||||||
@@ -41,3 +74,36 @@ func (r iteratorForCreateSysRolePermission) Err() error {
|
|||||||
func (q *Queries) CreateSysRolePermission(ctx context.Context, arg []CreateSysRolePermissionParams) (int64, error) {
|
func (q *Queries) CreateSysRolePermission(ctx context.Context, arg []CreateSysRolePermissionParams) (int64, error) {
|
||||||
return q.db.CopyFrom(ctx, []string{"sys_role_permission"}, []string{"role_id", "permission_id"}, &iteratorForCreateSysRolePermission{rows: arg})
|
return q.db.CopyFrom(ctx, []string{"sys_role_permission"}, []string{"role_id", "permission_id"}, &iteratorForCreateSysRolePermission{rows: arg})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iteratorForCreateSysUserRole implements pgx.CopyFromSource.
|
||||||
|
type iteratorForCreateSysUserRole struct {
|
||||||
|
rows []CreateSysUserRoleParams
|
||||||
|
skippedFirstNextCall bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *iteratorForCreateSysUserRole) Next() bool {
|
||||||
|
if len(r.rows) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !r.skippedFirstNextCall {
|
||||||
|
r.skippedFirstNextCall = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
r.rows = r.rows[1:]
|
||||||
|
return len(r.rows) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r iteratorForCreateSysUserRole) Values() ([]interface{}, error) {
|
||||||
|
return []interface{}{
|
||||||
|
r.rows[0].UserID,
|
||||||
|
r.rows[0].RoleID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r iteratorForCreateSysUserRole) Err() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateSysUserRole(ctx context.Context, arg []CreateSysUserRoleParams) (int64, error) {
|
||||||
|
return q.db.CopyFrom(ctx, []string{"sys_user_role"}, []string{"user_id", "role_id"}, &iteratorForCreateSysUserRole{rows: arg})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: file.sql
|
// source: files.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -16,6 +16,8 @@ type Category struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// 分类编码
|
// 分类编码
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
// 分类排序
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
@@ -80,6 +82,13 @@ type PostStat struct {
|
|||||||
View int32 `json:"view"`
|
View int32 `json:"view"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PostTag struct {
|
||||||
|
// 文章ID
|
||||||
|
PostID int32 `json:"post_id"`
|
||||||
|
// 标签id
|
||||||
|
TagID int32 `json:"tag_id"`
|
||||||
|
}
|
||||||
|
|
||||||
type PostViewer struct {
|
type PostViewer struct {
|
||||||
// 文章id
|
// 文章id
|
||||||
PostID int32 `json:"post_id"`
|
PostID int32 `json:"post_id"`
|
||||||
@@ -121,7 +130,7 @@ type SysMenu struct {
|
|||||||
// 菜单名称
|
// 菜单名称
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// 菜单路径
|
// 菜单路径
|
||||||
Path string `json:"path"`
|
Path *string `json:"path"`
|
||||||
// 组件路径
|
// 组件路径
|
||||||
Component *string `json:"component"`
|
Component *string `json:"component"`
|
||||||
// 菜单类型 0: 目录 1: 菜单 2: button
|
// 菜单类型 0: 目录 1: 菜单 2: button
|
||||||
@@ -156,7 +165,7 @@ type SysPermission struct {
|
|||||||
Code *string `json:"code"`
|
Code *string `json:"code"`
|
||||||
Type int16 `json:"type"`
|
Type int16 `json:"type"`
|
||||||
// 创建时间
|
// 创建时间
|
||||||
CreatedAt interface{} `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
// 更新时间
|
// 更新时间
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
@@ -168,6 +177,8 @@ type SysRole struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// 角色编码,唯一
|
// 角色编码,唯一
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
// 角色状态 0:禁用 1:启用
|
||||||
|
Status int16 `json:"status"`
|
||||||
// 创建时间
|
// 创建时间
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
// 更新时间
|
// 更新时间
|
||||||
@@ -190,6 +201,8 @@ type SysUser struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
// hash密码
|
// hash密码
|
||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
|
// 用户状态 0:禁用 1:启用
|
||||||
|
Status int16 `json:"status"`
|
||||||
// 头像文件id
|
// 头像文件id
|
||||||
AvatarID *int32 `json:"avatar_id"`
|
AvatarID *int32 `json:"avatar_id"`
|
||||||
// 创建时间
|
// 创建时间
|
||||||
@@ -204,3 +217,16 @@ type SysUserRole struct {
|
|||||||
// 角色ID
|
// 角色ID
|
||||||
RoleID int32 `json:"role_id"`
|
RoleID int32 `json:"role_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
// 标签ID
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
// 标签名称
|
||||||
|
Name string `json:"name"`
|
||||||
|
// 标签编码
|
||||||
|
Code string `json:"code"`
|
||||||
|
// 标签排序
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: post.sql
|
// source: post.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -87,12 +87,21 @@ const getPostById = `-- name: GetPostById :one
|
|||||||
SELECT p.id, p.title, p.cover_id, p.slug, p.content, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
SELECT p.id, p.title, p.cover_id, p.slug, p.content, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
||||||
f.file_path AS cover,
|
f.file_path AS cover,
|
||||||
c.name AS category_name,
|
c.name AS category_name,
|
||||||
c.id AS category_id
|
c.id AS category_id,
|
||||||
|
COALESCE(
|
||||||
|
array_agg(pt.tag_id ORDER BY pt.tag_id) FILTER (WHERE pt.tag_id IS NOT NULL),
|
||||||
|
ARRAY[]::integer[]
|
||||||
|
) AS tags
|
||||||
FROM posts p
|
FROM posts p
|
||||||
LEFT JOIN files f ON f.id = p.cover_id
|
LEFT JOIN files f ON f.id = p.cover_id
|
||||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||||
LEFT JOIN categories c ON c.id = pc.category_id
|
LEFT JOIN categories c ON c.id = pc.category_id
|
||||||
|
LEFT JOIN post_tag pt ON pt.post_id = p.id
|
||||||
WHERE p.id = $1
|
WHERE p.id = $1
|
||||||
|
GROUP BY p.id,
|
||||||
|
f.file_path,
|
||||||
|
c.name,
|
||||||
|
c.id
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -111,6 +120,7 @@ type GetPostByIdRow struct {
|
|||||||
Cover *string `json:"cover"`
|
Cover *string `json:"cover"`
|
||||||
CategoryName *string `json:"category_name"`
|
CategoryName *string `json:"category_name"`
|
||||||
CategoryID *int32 `json:"category_id"`
|
CategoryID *int32 `json:"category_id"`
|
||||||
|
Tags interface{} `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetPostById(ctx context.Context, id int32) (GetPostByIdRow, error) {
|
func (q *Queries) GetPostById(ctx context.Context, id int32) (GetPostByIdRow, error) {
|
||||||
@@ -131,6 +141,7 @@ func (q *Queries) GetPostById(ctx context.Context, id int32) (GetPostByIdRow, er
|
|||||||
&i.Cover,
|
&i.Cover,
|
||||||
&i.CategoryName,
|
&i.CategoryName,
|
||||||
&i.CategoryID,
|
&i.CategoryID,
|
||||||
|
&i.Tags,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@@ -252,7 +263,9 @@ FROM
|
|||||||
AND p.published_at < NOW()
|
AND p.published_at < NOW()
|
||||||
GROUP BY
|
GROUP BY
|
||||||
c.id,
|
c.id,
|
||||||
|
c.sort,
|
||||||
c.name
|
c.name
|
||||||
|
ORDER BY c.sort DESC, c.id
|
||||||
`
|
`
|
||||||
|
|
||||||
type ListCategoryStatsRow struct {
|
type ListCategoryStatsRow struct {
|
||||||
@@ -296,17 +309,45 @@ WITH paginated_posts AS (
|
|||||||
updated_at
|
updated_at
|
||||||
FROM posts
|
FROM posts
|
||||||
ORDER BY sort DESC, published_at DESC, id DESC
|
ORDER BY sort DESC, published_at DESC, id DESC
|
||||||
LIMIT $1 OFFSET $2)
|
LIMIT $1 OFFSET $2
|
||||||
|
)
|
||||||
SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.status, p.sort, p.published_at, p.created_at, p.updated_at,
|
||||||
f.file_path AS cover,
|
f.file_path AS cover,
|
||||||
c.name AS category_name,
|
c.name AS category_name,
|
||||||
c.id AS category_id,
|
c.id AS category_id,
|
||||||
COALESCE(ps.view, 0) AS view
|
COALESCE(ps.view, 0) AS view,
|
||||||
|
COALESCE(
|
||||||
|
jsonb_agg(
|
||||||
|
jsonb_build_object(
|
||||||
|
'id', t.id,
|
||||||
|
'name', t.name,
|
||||||
|
'code', t.code
|
||||||
|
)
|
||||||
|
ORDER BY t.sort DESC, t.id
|
||||||
|
) FILTER (WHERE t.id IS NOT NULL),
|
||||||
|
'[]'::jsonb
|
||||||
|
) AS tags
|
||||||
FROM paginated_posts p
|
FROM paginated_posts p
|
||||||
LEFT JOIN files f ON f.id = p.cover_id
|
LEFT JOIN files f ON f.id = p.cover_id
|
||||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||||
LEFT JOIN categories c ON c.id = pc.category_id
|
LEFT JOIN categories c ON c.id = pc.category_id
|
||||||
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
||||||
|
LEFT JOIN post_tag pt ON pt.post_id = p.id
|
||||||
|
LEFT JOIN tags t ON t.id = pt.tag_id
|
||||||
|
GROUP BY p.id,
|
||||||
|
p.title,
|
||||||
|
p.cover_id,
|
||||||
|
p.slug,
|
||||||
|
p.summary,
|
||||||
|
p.status,
|
||||||
|
p.sort,
|
||||||
|
p.published_at,
|
||||||
|
p.created_at,
|
||||||
|
p.updated_at,
|
||||||
|
f.file_path,
|
||||||
|
c.name,
|
||||||
|
c.id,
|
||||||
|
ps.view
|
||||||
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC
|
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -330,6 +371,7 @@ type ListPostsRow struct {
|
|||||||
CategoryName *string `json:"category_name"`
|
CategoryName *string `json:"category_name"`
|
||||||
CategoryID *int32 `json:"category_id"`
|
CategoryID *int32 `json:"category_id"`
|
||||||
View int32 `json:"view"`
|
View int32 `json:"view"`
|
||||||
|
Tags interface{} `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第二步:用这极少量的记录去进行 JOIN
|
// 第二步:用这极少量的记录去进行 JOIN
|
||||||
@@ -357,6 +399,7 @@ func (q *Queries) ListPosts(ctx context.Context, arg ListPostsParams) ([]ListPos
|
|||||||
&i.CategoryName,
|
&i.CategoryName,
|
||||||
&i.CategoryID,
|
&i.CategoryID,
|
||||||
&i.View,
|
&i.View,
|
||||||
|
&i.Tags,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -387,12 +430,36 @@ SELECT p.id, p.title, p.cover_id, p.slug, p.summary, p.sort, p.published_at,
|
|||||||
f.file_path AS cover,
|
f.file_path AS cover,
|
||||||
c.name AS category_name,
|
c.name AS category_name,
|
||||||
c.id AS category_id,
|
c.id AS category_id,
|
||||||
COALESCE(ps.view, 0) AS view
|
COALESCE(ps.view, 0) AS view,
|
||||||
|
COALESCE(
|
||||||
|
jsonb_agg(
|
||||||
|
jsonb_build_object(
|
||||||
|
'id', t.id,
|
||||||
|
'name', t.name,
|
||||||
|
'code', t.code
|
||||||
|
)
|
||||||
|
ORDER BY t.sort DESC, t.id
|
||||||
|
) FILTER (WHERE t.id IS NOT NULL),
|
||||||
|
'[]'::jsonb
|
||||||
|
) AS tags
|
||||||
FROM paginated_posts p
|
FROM paginated_posts p
|
||||||
LEFT JOIN files f ON f.id = p.cover_id
|
LEFT JOIN files f ON f.id = p.cover_id
|
||||||
LEFT JOIN post_category pc ON pc.post_id = p.id
|
LEFT JOIN post_category pc ON pc.post_id = p.id
|
||||||
LEFT JOIN categories c ON c.id = pc.category_id
|
LEFT JOIN categories c ON c.id = pc.category_id
|
||||||
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
LEFT JOIN post_stats ps ON ps.post_id = p.id
|
||||||
|
LEFT JOIN post_tag pt ON pt.post_id = p.id
|
||||||
|
LEFT JOIN tags t ON t.id = pt.tag_id
|
||||||
|
GROUP BY p.id,
|
||||||
|
p.title,
|
||||||
|
p.cover_id,
|
||||||
|
p.slug,
|
||||||
|
p.summary,
|
||||||
|
p.sort,
|
||||||
|
p.published_at,
|
||||||
|
f.file_path,
|
||||||
|
c.name,
|
||||||
|
c.id,
|
||||||
|
ps.view
|
||||||
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC
|
ORDER BY p.sort DESC, p.published_at DESC, p.id DESC
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -413,6 +480,7 @@ type ListPublishedPostsRow struct {
|
|||||||
CategoryName *string `json:"category_name"`
|
CategoryName *string `json:"category_name"`
|
||||||
CategoryID *int32 `json:"category_id"`
|
CategoryID *int32 `json:"category_id"`
|
||||||
View int32 `json:"view"`
|
View int32 `json:"view"`
|
||||||
|
Tags interface{} `json:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ListPublishedPosts(ctx context.Context, arg ListPublishedPostsParams) ([]ListPublishedPostsRow, error) {
|
func (q *Queries) ListPublishedPosts(ctx context.Context, arg ListPublishedPostsParams) ([]ListPublishedPostsRow, error) {
|
||||||
@@ -436,6 +504,7 @@ func (q *Queries) ListPublishedPosts(ctx context.Context, arg ListPublishedPosts
|
|||||||
&i.CategoryName,
|
&i.CategoryName,
|
||||||
&i.CategoryID,
|
&i.CategoryID,
|
||||||
&i.View,
|
&i.View,
|
||||||
|
&i.Tags,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: post_stats.sql
|
// source: post_stats.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: sys_api.sql
|
// source: sys_apis.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -12,10 +12,17 @@ import (
|
|||||||
const countSysApis = `-- name: CountSysApis :one
|
const countSysApis = `-- name: CountSysApis :one
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM sys_apis
|
FROM sys_apis
|
||||||
|
WHERE ($1::text = '' OR group_name ILIKE '%' || $1 || '%')
|
||||||
|
AND ($2::text = '' OR method = $2)
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CountSysApis(ctx context.Context) (int64, error) {
|
type CountSysApisParams struct {
|
||||||
row := q.db.QueryRow(ctx, countSysApis)
|
GroupName string `json:"group_name"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CountSysApis(ctx context.Context, arg CountSysApisParams) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countSysApis, arg.GroupName, arg.Method)
|
||||||
var count int64
|
var count int64
|
||||||
err := row.Scan(&count)
|
err := row.Scan(&count)
|
||||||
return count, err
|
return count, err
|
||||||
@@ -88,19 +95,6 @@ func (q *Queries) DeleteSysApiPermission(ctx context.Context, apiID int32) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteSysPermissionBySysApiID = `-- name: DeleteSysPermissionBySysApiID :exec
|
|
||||||
DELETE
|
|
||||||
FROM sys_permissions
|
|
||||||
WHERE id IN (SELECT permission_id
|
|
||||||
FROM sys_api_permission
|
|
||||||
WHERE api_id = $1)
|
|
||||||
`
|
|
||||||
|
|
||||||
func (q *Queries) DeleteSysPermissionBySysApiID(ctx context.Context, apiID int32) error {
|
|
||||||
_, err := q.db.Exec(ctx, deleteSysPermissionBySysApiID, apiID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAllSysApis = `-- name: GetAllSysApis :many
|
const getAllSysApis = `-- name: GetAllSysApis :many
|
||||||
SELECT id, group_name, name, method, path, sort, created_at, updated_at
|
SELECT id, group_name, name, method, path, sort, created_at, updated_at
|
||||||
FROM sys_apis
|
FROM sys_apis
|
||||||
@@ -186,6 +180,8 @@ func (q *Queries) GetSysApiGroupNames(ctx context.Context) ([]string, error) {
|
|||||||
const getSysApis = `-- name: GetSysApis :many
|
const getSysApis = `-- name: GetSysApis :many
|
||||||
SELECT id, group_name, name, method, path, sort, created_at, updated_at
|
SELECT id, group_name, name, method, path, sort, created_at, updated_at
|
||||||
FROM sys_apis
|
FROM sys_apis
|
||||||
|
WHERE ($3::text = '' OR group_name ILIKE '%' || $3 || '%')
|
||||||
|
AND ($4::text = '' OR method = $4)
|
||||||
ORDER BY id
|
ORDER BY id
|
||||||
LIMIT $1 OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
@@ -193,10 +189,17 @@ LIMIT $1 OFFSET $2
|
|||||||
type GetSysApisParams struct {
|
type GetSysApisParams struct {
|
||||||
Limit int32 `json:"limit"`
|
Limit int32 `json:"limit"`
|
||||||
Offset int32 `json:"offset"`
|
Offset int32 `json:"offset"`
|
||||||
|
GroupName string `json:"group_name"`
|
||||||
|
Method string `json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetSysApis(ctx context.Context, arg GetSysApisParams) ([]SysApi, error) {
|
func (q *Queries) GetSysApis(ctx context.Context, arg GetSysApisParams) ([]SysApi, error) {
|
||||||
rows, err := q.db.Query(ctx, getSysApis, arg.Limit, arg.Offset)
|
rows, err := q.db.Query(ctx, getSysApis,
|
||||||
|
arg.Limit,
|
||||||
|
arg.Offset,
|
||||||
|
arg.GroupName,
|
||||||
|
arg.Method,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: sys_menu.sql
|
// source: sys_menus.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -30,7 +30,7 @@ RETURNING id
|
|||||||
|
|
||||||
type CreateSysMenuParams struct {
|
type CreateSysMenuParams struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path *string `json:"path"`
|
||||||
Component *string `json:"component"`
|
Component *string `json:"component"`
|
||||||
Type int16 `json:"type"`
|
Type int16 `json:"type"`
|
||||||
Hidden *bool `json:"hidden"`
|
Hidden *bool `json:"hidden"`
|
||||||
@@ -123,7 +123,7 @@ ORDER BY m.sort ASC,
|
|||||||
type GetAllSysMenusRow struct {
|
type GetAllSysMenusRow struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path *string `json:"path"`
|
||||||
Component *string `json:"component"`
|
Component *string `json:"component"`
|
||||||
Type int16 `json:"type"`
|
Type int16 `json:"type"`
|
||||||
Hidden *bool `json:"hidden"`
|
Hidden *bool `json:"hidden"`
|
||||||
@@ -213,7 +213,7 @@ type ListSysMenusParams struct {
|
|||||||
type ListSysMenusRow struct {
|
type ListSysMenusRow struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path *string `json:"path"`
|
||||||
Component *string `json:"component"`
|
Component *string `json:"component"`
|
||||||
Type int16 `json:"type"`
|
Type int16 `json:"type"`
|
||||||
Hidden *bool `json:"hidden"`
|
Hidden *bool `json:"hidden"`
|
||||||
@@ -269,9 +269,9 @@ SET name = coalesce($1, name),
|
|||||||
hidden = coalesce($5, hidden),
|
hidden = coalesce($5, hidden),
|
||||||
sort = coalesce($6, sort),
|
sort = coalesce($6, sort),
|
||||||
status = coalesce($7, status),
|
status = coalesce($7, status),
|
||||||
icon = coalesce($8, status),
|
icon = CASE WHEN $8::boolean THEN $9 ELSE icon END,
|
||||||
parent_id = CASE WHEN $9::boolean THEN $10 ELSE parent_id END
|
parent_id = CASE WHEN $10::boolean THEN $11 ELSE parent_id END
|
||||||
WHERE id = $11
|
WHERE id = $12
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateSysMenuParams struct {
|
type UpdateSysMenuParams struct {
|
||||||
@@ -282,6 +282,7 @@ type UpdateSysMenuParams struct {
|
|||||||
Hidden *bool `json:"hidden"`
|
Hidden *bool `json:"hidden"`
|
||||||
Sort *int32 `json:"sort"`
|
Sort *int32 `json:"sort"`
|
||||||
Status *int16 `json:"status"`
|
Status *int16 `json:"status"`
|
||||||
|
UpdateIcon bool `json:"update_icon"`
|
||||||
Icon *int32 `json:"icon"`
|
Icon *int32 `json:"icon"`
|
||||||
UpdateParentID bool `json:"update_parent_id"`
|
UpdateParentID bool `json:"update_parent_id"`
|
||||||
ParentID *int32 `json:"parent_id"`
|
ParentID *int32 `json:"parent_id"`
|
||||||
@@ -297,6 +298,7 @@ func (q *Queries) UpdateSysMenu(ctx context.Context, arg UpdateSysMenuParams) (i
|
|||||||
arg.Hidden,
|
arg.Hidden,
|
||||||
arg.Sort,
|
arg.Sort,
|
||||||
arg.Status,
|
arg.Status,
|
||||||
|
arg.UpdateIcon,
|
||||||
arg.Icon,
|
arg.Icon,
|
||||||
arg.UpdateParentID,
|
arg.UpdateParentID,
|
||||||
arg.ParentID,
|
arg.ParentID,
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: sys_permission.sql
|
// source: sys_permissions.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -37,6 +37,32 @@ func (q *Queries) DeleteSysPermission(ctx context.Context, id int32) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSysPermissionIDBySysApiID = `-- name: GetSysPermissionIDBySysApiID :one
|
||||||
|
SELECT permission_id
|
||||||
|
FROM sys_api_permission
|
||||||
|
WHERE api_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSysPermissionIDBySysApiID(ctx context.Context, apiID int32) (int32, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getSysPermissionIDBySysApiID, apiID)
|
||||||
|
var permission_id int32
|
||||||
|
err := row.Scan(&permission_id)
|
||||||
|
return permission_id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSysPermissionIDBySysMenuID = `-- name: GetSysPermissionIDBySysMenuID :one
|
||||||
|
SELECT permission_id
|
||||||
|
FROM sys_menu_permission
|
||||||
|
WHERE menu_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSysPermissionIDBySysMenuID(ctx context.Context, menuID int32) (int32, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getSysPermissionIDBySysMenuID, menuID)
|
||||||
|
var permission_id int32
|
||||||
|
err := row.Scan(&permission_id)
|
||||||
|
return permission_id, err
|
||||||
|
}
|
||||||
|
|
||||||
const updateSysPermission = `-- name: UpdateSysPermission :exec
|
const updateSysPermission = `-- name: UpdateSysPermission :exec
|
||||||
UPDATE sys_permissions
|
UPDATE sys_permissions
|
||||||
SET code = $2
|
SET code = $2
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: sys_role.sql
|
// source: sys_roles.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -22,17 +22,18 @@ func (q *Queries) CountSysRoles(ctx context.Context) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createSysRole = `-- name: CreateSysRole :exec
|
const createSysRole = `-- name: CreateSysRole :exec
|
||||||
INSERT INTO sys_roles (name, code)
|
INSERT INTO sys_roles (name, code, status)
|
||||||
VALUES ($1, $2)
|
VALUES ($1, $2, $3)
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateSysRoleParams struct {
|
type CreateSysRoleParams struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
Status int16 `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateSysRole(ctx context.Context, arg CreateSysRoleParams) error {
|
func (q *Queries) CreateSysRole(ctx context.Context, arg CreateSysRoleParams) error {
|
||||||
_, err := q.db.Exec(ctx, createSysRole, arg.Name, arg.Code)
|
_, err := q.db.Exec(ctx, createSysRole, arg.Name, arg.Code, arg.Status)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,21 +56,54 @@ func (q *Queries) DeleteSysRole(ctx context.Context, id int32) (int64, error) {
|
|||||||
return result.RowsAffected(), nil
|
return result.RowsAffected(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteSysRolePermission = `-- name: DeleteSysRolePermission :exec
|
const deleteSysRolePermissionByPermissionID = `-- name: DeleteSysRolePermissionByPermissionID :exec
|
||||||
|
DELETE
|
||||||
|
FROM sys_role_permission
|
||||||
|
WHERE permission_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteSysRolePermissionByPermissionID(ctx context.Context, permissionID int32) error {
|
||||||
|
_, err := q.db.Exec(ctx, deleteSysRolePermissionByPermissionID, permissionID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSysRolePermissionByRoleID = `-- name: DeleteSysRolePermissionByRoleID :exec
|
||||||
|
DELETE
|
||||||
|
FROM sys_role_permission
|
||||||
|
WHERE role_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteSysRolePermissionByRoleID(ctx context.Context, roleID int32) error {
|
||||||
|
_, err := q.db.Exec(ctx, deleteSysRolePermissionByRoleID, roleID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSysRolePermissionByType = `-- name: DeleteSysRolePermissionByType :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM sys_role_permission rp USING sys_permissions p
|
FROM sys_role_permission rp USING sys_permissions p
|
||||||
WHERE rp.permission_id = p.id
|
WHERE rp.permission_id = p.id
|
||||||
AND p."type" = $2
|
|
||||||
AND rp.role_id = $1
|
AND rp.role_id = $1
|
||||||
|
AND p."type" = $2
|
||||||
`
|
`
|
||||||
|
|
||||||
type DeleteSysRolePermissionParams struct {
|
type DeleteSysRolePermissionByTypeParams struct {
|
||||||
RoleID int32 `json:"role_id"`
|
RoleID int32 `json:"role_id"`
|
||||||
Type int16 `json:"type"`
|
Type int16 `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) DeleteSysRolePermission(ctx context.Context, arg DeleteSysRolePermissionParams) error {
|
func (q *Queries) DeleteSysRolePermissionByType(ctx context.Context, arg DeleteSysRolePermissionByTypeParams) error {
|
||||||
_, err := q.db.Exec(ctx, deleteSysRolePermission, arg.RoleID, arg.Type)
|
_, err := q.db.Exec(ctx, deleteSysRolePermissionByType, arg.RoleID, arg.Type)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSysUserRoleByRoleID = `-- name: DeleteSysUserRoleByRoleID :exec
|
||||||
|
DELETE
|
||||||
|
FROM sys_user_role
|
||||||
|
WHERE role_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteSysUserRoleByRoleID(ctx context.Context, roleID int32) error {
|
||||||
|
_, err := q.db.Exec(ctx, deleteSysUserRoleByRoleID, roleID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +130,7 @@ func (q *Queries) ExistsSysRoleCode(ctx context.Context, arg ExistsSysRoleCodePa
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAllSysRoles = `-- name: GetAllSysRoles :many
|
const getAllSysRoles = `-- name: GetAllSysRoles :many
|
||||||
SELECT id, name, code, created_at, updated_at
|
SELECT id, name, code, status, created_at, updated_at
|
||||||
from sys_roles
|
from sys_roles
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -113,6 +147,7 @@ func (q *Queries) GetAllSysRoles(ctx context.Context) ([]SysRole, error) {
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Code,
|
&i.Code,
|
||||||
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -212,7 +247,7 @@ func (q *Queries) GetSysRoleApis(ctx context.Context, roleID int32) ([]GetSysRol
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getSysRoleByID = `-- name: GetSysRoleByID :one
|
const getSysRoleByID = `-- name: GetSysRoleByID :one
|
||||||
SELECT id, name, code, created_at, updated_at
|
SELECT id, name, code, status, created_at, updated_at
|
||||||
FROM sys_roles
|
FROM sys_roles
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
@@ -224,6 +259,7 @@ func (q *Queries) GetSysRoleByID(ctx context.Context, id int32) (SysRole, error)
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Code,
|
&i.Code,
|
||||||
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
)
|
)
|
||||||
@@ -264,7 +300,7 @@ func (q *Queries) GetSysRoleMenus(ctx context.Context, roleID int32) ([]GetSysRo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listSysRoles = `-- name: ListSysRoles :many
|
const listSysRoles = `-- name: ListSysRoles :many
|
||||||
SELECT id, name, code, created_at, updated_at
|
SELECT id, name, code, status, created_at, updated_at
|
||||||
FROM sys_roles
|
FROM sys_roles
|
||||||
ORDER BY id
|
ORDER BY id
|
||||||
LIMIT $1 OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
@@ -288,6 +324,7 @@ func (q *Queries) ListSysRoles(ctx context.Context, arg ListSysRolesParams) ([]S
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Code,
|
&i.Code,
|
||||||
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -303,17 +340,19 @@ func (q *Queries) ListSysRoles(ctx context.Context, arg ListSysRolesParams) ([]S
|
|||||||
|
|
||||||
const updateSysRole = `-- name: UpdateSysRole :execrows
|
const updateSysRole = `-- name: UpdateSysRole :execrows
|
||||||
UPDATE sys_roles
|
UPDATE sys_roles
|
||||||
SET name = coalesce($1, name)
|
SET name = coalesce($1, name),
|
||||||
WHERE id = $2
|
status = coalesce($2, status)
|
||||||
|
WHERE id = $3
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateSysRoleParams struct {
|
type UpdateSysRoleParams struct {
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
|
Status *int16 `json:"status"`
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) UpdateSysRole(ctx context.Context, arg UpdateSysRoleParams) (int64, error) {
|
func (q *Queries) UpdateSysRole(ctx context.Context, arg UpdateSysRoleParams) (int64, error) {
|
||||||
result, err := q.db.Exec(ctx, updateSysRole, arg.Name, arg.ID)
|
result, err := q.db.Exec(ctx, updateSysRole, arg.Name, arg.Status, arg.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.31.1
|
// sqlc v1.31.1
|
||||||
// source: sys_user.sql
|
// source: sys_users.sql
|
||||||
|
|
||||||
package db
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -24,24 +24,26 @@ func (q *Queries) ClearSysUserRoles(ctx context.Context, userID int32) error {
|
|||||||
const countSysUsers = `-- name: CountSysUsers :one
|
const countSysUsers = `-- name: CountSysUsers :one
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM sys_users
|
FROM sys_users
|
||||||
|
WHERE ($1::text = '' OR username ILIKE '%' || $1 || '%')
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) CountSysUsers(ctx context.Context) (int64, error) {
|
func (q *Queries) CountSysUsers(ctx context.Context, username string) (int64, error) {
|
||||||
row := q.db.QueryRow(ctx, countSysUsers)
|
row := q.db.QueryRow(ctx, countSysUsers, username)
|
||||||
var count int64
|
var count int64
|
||||||
err := row.Scan(&count)
|
err := row.Scan(&count)
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const createSysUser = `-- name: CreateSysUser :exec
|
const createSysUser = `-- name: CreateSysUser :exec
|
||||||
INSERT INTO sys_users (account, username, password_hash, avatar_id)
|
INSERT INTO sys_users (account, username, password_hash, status, avatar_id)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateSysUserParams struct {
|
type CreateSysUserParams struct {
|
||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
|
Status int16 `json:"status"`
|
||||||
AvatarID *int32 `json:"avatar_id"`
|
AvatarID *int32 `json:"avatar_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,26 +52,17 @@ func (q *Queries) CreateSysUser(ctx context.Context, arg CreateSysUserParams) er
|
|||||||
arg.Account,
|
arg.Account,
|
||||||
arg.Username,
|
arg.Username,
|
||||||
arg.PasswordHash,
|
arg.PasswordHash,
|
||||||
|
arg.Status,
|
||||||
arg.AvatarID,
|
arg.AvatarID,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const createSysUserRole = `-- name: CreateSysUserRole :exec
|
|
||||||
INSERT INTO sys_user_role (user_id, role_id)
|
|
||||||
VALUES ($1, $2)
|
|
||||||
`
|
|
||||||
|
|
||||||
type CreateSysUserRoleParams struct {
|
type CreateSysUserRoleParams struct {
|
||||||
UserID int32 `json:"user_id"`
|
UserID int32 `json:"user_id"`
|
||||||
RoleID int32 `json:"role_id"`
|
RoleID int32 `json:"role_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateSysUserRole(ctx context.Context, arg CreateSysUserRoleParams) error {
|
|
||||||
_, err := q.db.Exec(ctx, createSysUserRole, arg.UserID, arg.RoleID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteSysUser = `-- name: DeleteSysUser :execrows
|
const deleteSysUser = `-- name: DeleteSysUser :execrows
|
||||||
DELETE
|
DELETE
|
||||||
FROM sys_users
|
FROM sys_users
|
||||||
@@ -85,9 +78,38 @@ func (q *Queries) DeleteSysUser(ctx context.Context, id int32) (int64, error) {
|
|||||||
return result.RowsAffected(), nil
|
return result.RowsAffected(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getActiveSysUserByID = `-- name: GetActiveSysUserByID :one
|
||||||
|
SELECT id, account, username, status
|
||||||
|
FROM sys_users
|
||||||
|
WHERE id = $1
|
||||||
|
AND status = 1
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetActiveSysUserByIDRow struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Account string `json:"account"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Status int16 `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 场景:用户登录、获取个人信息、刷新 Token(严格校验 status = 1)
|
||||||
|
func (q *Queries) GetActiveSysUserByID(ctx context.Context, id int32) (GetActiveSysUserByIDRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, getActiveSysUserByID, id)
|
||||||
|
var i GetActiveSysUserByIDRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Account,
|
||||||
|
&i.Username,
|
||||||
|
&i.Status,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getSysAdminMenus = `-- name: GetSysAdminMenus :many
|
const getSysAdminMenus = `-- name: GetSysAdminMenus :many
|
||||||
SELECT id, name, path, component, type, hidden, sort, status, parent_id, icon, created_at, updated_at
|
SELECT id, name, path, component, type, hidden, sort, status, parent_id, icon, created_at, updated_at
|
||||||
FROM sys_menus
|
FROM sys_menus
|
||||||
|
WHERE status = 1
|
||||||
|
AND type <> 2
|
||||||
ORDER BY sort, id
|
ORDER BY sort, id
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -124,18 +146,45 @@ func (q *Queries) GetSysAdminMenus(ctx context.Context) ([]SysMenu, error) {
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSysAdminPermissions = `-- name: GetSysAdminPermissions :many
|
||||||
|
SELECT code
|
||||||
|
FROM sys_permissions
|
||||||
|
WHERE type = 0
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSysAdminPermissions(ctx context.Context) ([]*string, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getSysAdminPermissions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []*string{}
|
||||||
|
for rows.Next() {
|
||||||
|
var code *string
|
||||||
|
if err := rows.Scan(&code); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, code)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getSysUserApis = `-- name: GetSysUserApis :many
|
const getSysUserApis = `-- name: GetSysUserApis :many
|
||||||
SELECT DISTINCT a.id,
|
SELECT DISTINCT a.id,
|
||||||
a.PATH,
|
a.PATH,
|
||||||
a.METHOD
|
a.METHOD
|
||||||
FROM sys_users u
|
FROM sys_users u
|
||||||
JOIN sys_user_role ur ON u.id = ur.user_id
|
JOIN sys_user_role ur ON u.id = ur.user_id
|
||||||
JOIN sys_role_permission rp ON rp.role_id = ur.role_id
|
JOIN sys_roles r ON r.id = ur.role_id
|
||||||
JOIN sys_permissions p ON p.id = rp.permission_id
|
JOIN sys_role_permission rp ON rp.role_id = r.id
|
||||||
AND p.TYPE = 1
|
JOIN sys_api_permission ap ON ap.permission_id = rp.permission_id
|
||||||
JOIN sys_api_permission ap ON ap.permission_id = p.id
|
|
||||||
JOIN sys_apis a ON a.id = ap.api_id
|
JOIN sys_apis a ON a.id = ap.api_id
|
||||||
WHERE u.id = $1
|
WHERE u.id = $1
|
||||||
|
AND u.status = 1
|
||||||
|
AND r.status = 1
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetSysUserApisRow struct {
|
type GetSysUserApisRow struct {
|
||||||
@@ -169,6 +218,7 @@ SELECT u.id,
|
|||||||
u.account,
|
u.account,
|
||||||
u.username,
|
u.username,
|
||||||
u.password_hash,
|
u.password_hash,
|
||||||
|
u.status,
|
||||||
u.avatar_id,
|
u.avatar_id,
|
||||||
f.file_path AS avatar_url,
|
f.file_path AS avatar_url,
|
||||||
u.created_at,
|
u.created_at,
|
||||||
@@ -183,6 +233,7 @@ type GetSysUserByAccountRow struct {
|
|||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
|
Status int16 `json:"status"`
|
||||||
AvatarID *int32 `json:"avatar_id"`
|
AvatarID *int32 `json:"avatar_id"`
|
||||||
AvatarUrl *string `json:"avatar_url"`
|
AvatarUrl *string `json:"avatar_url"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
@@ -197,6 +248,7 @@ func (q *Queries) GetSysUserByAccount(ctx context.Context, account string) (GetS
|
|||||||
&i.Account,
|
&i.Account,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.Status,
|
||||||
&i.AvatarID,
|
&i.AvatarID,
|
||||||
&i.AvatarUrl,
|
&i.AvatarUrl,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
@@ -210,6 +262,7 @@ SELECT u.id,
|
|||||||
u.account,
|
u.account,
|
||||||
u.username,
|
u.username,
|
||||||
u.avatar_id,
|
u.avatar_id,
|
||||||
|
u.status,
|
||||||
f.file_path AS avatar_url,
|
f.file_path AS avatar_url,
|
||||||
u.created_at,
|
u.created_at,
|
||||||
u.updated_at
|
u.updated_at
|
||||||
@@ -223,6 +276,7 @@ type GetSysUserByIDRow struct {
|
|||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
AvatarID *int32 `json:"avatar_id"`
|
AvatarID *int32 `json:"avatar_id"`
|
||||||
|
Status int16 `json:"status"`
|
||||||
AvatarUrl *string `json:"avatar_url"`
|
AvatarUrl *string `json:"avatar_url"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
@@ -236,6 +290,7 @@ func (q *Queries) GetSysUserByID(ctx context.Context, id int32) (GetSysUserByIDR
|
|||||||
&i.Account,
|
&i.Account,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.AvatarID,
|
&i.AvatarID,
|
||||||
|
&i.Status,
|
||||||
&i.AvatarUrl,
|
&i.AvatarUrl,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
@@ -249,10 +304,13 @@ FROM sys_users u
|
|||||||
JOIN sys_user_role ur ON ur.user_id = u.id
|
JOIN sys_user_role ur ON ur.user_id = u.id
|
||||||
JOIN sys_roles r ON r.id = ur.role_id
|
JOIN sys_roles r ON r.id = ur.role_id
|
||||||
JOIN sys_role_permission rp ON rp.role_id = r.id
|
JOIN sys_role_permission rp ON rp.role_id = r.id
|
||||||
JOIN sys_permissions p ON p.id = rp.permission_id
|
JOIN sys_menu_permission mp ON mp.permission_id = rp.permission_id
|
||||||
JOIN sys_menu_permission mp ON mp.permission_id = p.id
|
|
||||||
JOIN sys_menus m ON m.id = mp.menu_id
|
JOIN sys_menus m ON m.id = mp.menu_id
|
||||||
WHERE u.id = $1
|
WHERE u.id = $1
|
||||||
|
AND u.status = 1
|
||||||
|
AND r.status = 1
|
||||||
|
AND m.status = 1
|
||||||
|
AND m.type <> 2
|
||||||
ORDER BY m.sort, m.id
|
ORDER BY m.sort, m.id
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -289,8 +347,44 @@ func (q *Queries) GetSysUserMenus(ctx context.Context, id int32) ([]SysMenu, err
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSysUserPermissions = `-- name: GetSysUserPermissions :many
|
||||||
|
SELECT DISTINCT p.code
|
||||||
|
FROM sys_users u
|
||||||
|
JOIN sys_user_role ur ON u.id = ur.user_id
|
||||||
|
JOIN sys_roles r on r.id = ur.role_id
|
||||||
|
JOIN sys_role_permission rp ON rp.role_id = r.id
|
||||||
|
JOIN sys_permissions p ON p.id = rp.permission_id
|
||||||
|
JOIN sys_menu_permission mp ON mp.permission_id = p.id
|
||||||
|
JOIN sys_menus m ON m.id = mp.menu_id
|
||||||
|
WHERE u.id = $1
|
||||||
|
AND u.status = 1
|
||||||
|
AND r.status = 1
|
||||||
|
AND m.status = 1
|
||||||
|
AND p."type" = 0
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSysUserPermissions(ctx context.Context, id int32) ([]*string, error) {
|
||||||
|
rows, err := q.db.Query(ctx, getSysUserPermissions, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []*string{}
|
||||||
|
for rows.Next() {
|
||||||
|
var code *string
|
||||||
|
if err := rows.Scan(&code); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, code)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getSysUserRoles = `-- name: GetSysUserRoles :many
|
const getSysUserRoles = `-- name: GetSysUserRoles :many
|
||||||
SELECT r.id, r.name, r.code, r.created_at, r.updated_at
|
SELECT r.id, r.name, r.code, r.status, r.created_at, r.updated_at
|
||||||
FROM sys_roles r
|
FROM sys_roles r
|
||||||
JOIN sys_user_role ur ON r.id = ur.role_id
|
JOIN sys_user_role ur ON r.id = ur.role_id
|
||||||
WHERE ur.user_id = $1
|
WHERE ur.user_id = $1
|
||||||
@@ -309,6 +403,7 @@ func (q *Queries) GetSysUserRoles(ctx context.Context, userID int32) ([]SysRole,
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Name,
|
&i.Name,
|
||||||
&i.Code,
|
&i.Code,
|
||||||
|
&i.Status,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.UpdatedAt,
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -326,12 +421,14 @@ const listSysUsers = `-- name: ListSysUsers :many
|
|||||||
SELECT u.id,
|
SELECT u.id,
|
||||||
u.account,
|
u.account,
|
||||||
u.username,
|
u.username,
|
||||||
|
u.status,
|
||||||
u.avatar_id,
|
u.avatar_id,
|
||||||
f.file_path AS avatar_url,
|
f.file_path AS avatar_url,
|
||||||
u.created_at,
|
u.created_at,
|
||||||
u.updated_at
|
u.updated_at
|
||||||
FROM sys_users u
|
FROM sys_users u
|
||||||
LEFT JOIN files f ON u.avatar_id = f.id
|
LEFT JOIN files f ON u.avatar_id = f.id
|
||||||
|
WHERE ($3::text = '' OR u.username ILIKE '%' || $3 || '%')
|
||||||
ORDER BY u.id
|
ORDER BY u.id
|
||||||
LIMIT $1 OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
`
|
`
|
||||||
@@ -339,12 +436,14 @@ LIMIT $1 OFFSET $2
|
|||||||
type ListSysUsersParams struct {
|
type ListSysUsersParams struct {
|
||||||
Limit int32 `json:"limit"`
|
Limit int32 `json:"limit"`
|
||||||
Offset int32 `json:"offset"`
|
Offset int32 `json:"offset"`
|
||||||
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListSysUsersRow struct {
|
type ListSysUsersRow struct {
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
Status int16 `json:"status"`
|
||||||
AvatarID *int32 `json:"avatar_id"`
|
AvatarID *int32 `json:"avatar_id"`
|
||||||
AvatarUrl *string `json:"avatar_url"`
|
AvatarUrl *string `json:"avatar_url"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
@@ -352,7 +451,7 @@ type ListSysUsersRow struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ListSysUsers(ctx context.Context, arg ListSysUsersParams) ([]ListSysUsersRow, error) {
|
func (q *Queries) ListSysUsers(ctx context.Context, arg ListSysUsersParams) ([]ListSysUsersRow, error) {
|
||||||
rows, err := q.db.Query(ctx, listSysUsers, arg.Limit, arg.Offset)
|
rows, err := q.db.Query(ctx, listSysUsers, arg.Limit, arg.Offset, arg.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -364,6 +463,7 @@ func (q *Queries) ListSysUsers(ctx context.Context, arg ListSysUsersParams) ([]L
|
|||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Account,
|
&i.Account,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
|
&i.Status,
|
||||||
&i.AvatarID,
|
&i.AvatarID,
|
||||||
&i.AvatarUrl,
|
&i.AvatarUrl,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
@@ -382,12 +482,14 @@ func (q *Queries) ListSysUsers(ctx context.Context, arg ListSysUsersParams) ([]L
|
|||||||
const updateSysUser = `-- name: UpdateSysUser :execrows
|
const updateSysUser = `-- name: UpdateSysUser :execrows
|
||||||
UPDATE sys_users
|
UPDATE sys_users
|
||||||
SET username = coalesce($1, username),
|
SET username = coalesce($1, username),
|
||||||
avatar_id = CASE WHEN $2::boolean THEN $3 ELSE avatar_id END
|
status = coalesce($2, status),
|
||||||
WHERE id = $4
|
avatar_id = CASE WHEN $3::boolean THEN $4 ELSE avatar_id END
|
||||||
|
WHERE id = $5
|
||||||
`
|
`
|
||||||
|
|
||||||
type UpdateSysUserParams struct {
|
type UpdateSysUserParams struct {
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
|
Status *int16 `json:"status"`
|
||||||
UpdateAvatarID bool `json:"update_avatar_id"`
|
UpdateAvatarID bool `json:"update_avatar_id"`
|
||||||
AvatarID *int32 `json:"avatar_id"`
|
AvatarID *int32 `json:"avatar_id"`
|
||||||
ID int32 `json:"id"`
|
ID int32 `json:"id"`
|
||||||
@@ -396,6 +498,7 @@ type UpdateSysUserParams struct {
|
|||||||
func (q *Queries) UpdateSysUser(ctx context.Context, arg UpdateSysUserParams) (int64, error) {
|
func (q *Queries) UpdateSysUser(ctx context.Context, arg UpdateSysUserParams) (int64, error) {
|
||||||
result, err := q.db.Exec(ctx, updateSysUser,
|
result, err := q.db.Exec(ctx, updateSysUser,
|
||||||
arg.Username,
|
arg.Username,
|
||||||
|
arg.Status,
|
||||||
arg.UpdateAvatarID,
|
arg.UpdateAvatarID,
|
||||||
arg.AvatarID,
|
arg.AvatarID,
|
||||||
arg.ID,
|
arg.ID,
|
||||||
179
internal/db/sqlc/tags.sql.go
Normal file
179
internal/db/sqlc/tags.sql.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
// source: tags.sql
|
||||||
|
|
||||||
|
package sqlc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const countTags = `-- name: CountTags :one
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM tags
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountTags(ctx context.Context) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countTags)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreatePostTagParams struct {
|
||||||
|
PostID int32 `json:"post_id"`
|
||||||
|
TagID int32 `json:"tag_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTag = `-- name: CreateTag :exec
|
||||||
|
INSERT INTO tags(name, code, sort)
|
||||||
|
VALUES ($1, $2, $3)
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateTagParams struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateTag(ctx context.Context, arg CreateTagParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, createTag, arg.Name, arg.Code, arg.Sort)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletePostTagByPostID = `-- name: DeletePostTagByPostID :exec
|
||||||
|
DELETE
|
||||||
|
FROM post_tag
|
||||||
|
WHERE post_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeletePostTagByPostID(ctx context.Context, postID int32) error {
|
||||||
|
_, err := q.db.Exec(ctx, deletePostTagByPostID, postID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletePostTagByTagID = `-- name: DeletePostTagByTagID :exec
|
||||||
|
DELETE
|
||||||
|
FROM post_tag
|
||||||
|
WHERE tag_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeletePostTagByTagID(ctx context.Context, tagID int32) error {
|
||||||
|
_, err := q.db.Exec(ctx, deletePostTagByTagID, tagID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTag = `-- name: DeleteTag :execrows
|
||||||
|
DELETE
|
||||||
|
FROM tags
|
||||||
|
WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteTag(ctx context.Context, id int32) (int64, error) {
|
||||||
|
result, err := q.db.Exec(ctx, deleteTag, id)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return result.RowsAffected(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listAllTags = `-- name: ListAllTags :many
|
||||||
|
SELECT id, name, code, sort, created_at, updated_at
|
||||||
|
FROM tags
|
||||||
|
ORDER BY sort DESC, id
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListAllTags(ctx context.Context) ([]Tag, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listAllTags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []Tag{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i Tag
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Code,
|
||||||
|
&i.Sort,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listTags = `-- name: ListTags :many
|
||||||
|
SELECT id, name, code, sort, created_at, updated_at
|
||||||
|
FROM tags
|
||||||
|
ORDER BY sort DESC, id
|
||||||
|
LIMIT $1 OFFSET $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListTagsParams struct {
|
||||||
|
Limit int32 `json:"limit"`
|
||||||
|
Offset int32 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListTags(ctx context.Context, arg ListTagsParams) ([]Tag, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listTags, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []Tag{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i Tag
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Code,
|
||||||
|
&i.Sort,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTag = `-- name: UpdateTag :execrows
|
||||||
|
UPDATE tags
|
||||||
|
SET name = coalesce($1, name),
|
||||||
|
code = coalesce($2, code),
|
||||||
|
sort = coalesce($3, sort)
|
||||||
|
WHERE id = $4
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateTagParams struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Code *string `json:"code"`
|
||||||
|
Sort *int32 `json:"sort"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateTag(ctx context.Context, arg UpdateTagParams) (int64, error) {
|
||||||
|
result, err := q.db.Exec(ctx, updateTag,
|
||||||
|
arg.Name,
|
||||||
|
arg.Code,
|
||||||
|
arg.Sort,
|
||||||
|
arg.ID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return result.RowsAffected(), nil
|
||||||
|
}
|
||||||
68
internal/db/store.go
Normal file
68
internal/db/store.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
db "server/internal/db/sqlc"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store struct {
|
||||||
|
*db.Queries
|
||||||
|
pool *pgxpool.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(pool *pgxpool.Pool) *Store {
|
||||||
|
return &Store{
|
||||||
|
pool: pool,
|
||||||
|
Queries: db.New(pool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *Store) WithTx(ctx context.Context, fn func(q *db.Queries) error, opts ...pgx.TxOptions) error {
|
||||||
|
txOpt := pgx.TxOptions{}
|
||||||
|
if len(opts) > 0 {
|
||||||
|
txOpt = opts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := store.pool.BeginTx(ctx, txOpt)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("begin tx: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// 使用 context.Background() 确保即使请求 context 被取消,
|
||||||
|
// 回滚指令也能发送给 DB,从而安全地回收连接而不是销毁连接。
|
||||||
|
_ = tx.Rollback(context.Background())
|
||||||
|
}()
|
||||||
|
|
||||||
|
q := store.Queries.WithTx(tx)
|
||||||
|
|
||||||
|
if err := fn(q); err != nil {
|
||||||
|
return err // 业务侧的错误保持原样返回
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(ctx); err != nil {
|
||||||
|
return fmt.Errorf("commit tx: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTxResult 带返回值的事务
|
||||||
|
func WithTxResult[T any](ctx context.Context, store *Store, fn func(q *db.Queries) (T, error), opts ...pgx.TxOptions) (T, error) {
|
||||||
|
var result T
|
||||||
|
|
||||||
|
err := store.WithTx(ctx, func(q *db.Queries) error {
|
||||||
|
r, err := fn(q)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result = r
|
||||||
|
return nil
|
||||||
|
}, opts...)
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
96
internal/handler/auth.go
Normal file
96
internal/handler/auth.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"server/internal/config"
|
||||||
|
"server/internal/model/request"
|
||||||
|
"server/internal/pkg/errs"
|
||||||
|
"server/internal/pkg/httputil"
|
||||||
|
"server/internal/service"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthHandler struct {
|
||||||
|
authService *service.AuthService
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthHandler(authService *service.AuthService, cfg *config.Config) *AuthHandler {
|
||||||
|
return &AuthHandler{authService: authService, cfg: cfg}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) clearRefreshTokenCookie(w http.ResponseWriter) {
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: h.cfg.JWTConfig.CookieName,
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
Secure: true,
|
||||||
|
SameSite: http.SameSiteNoneMode,
|
||||||
|
MaxAge: -1,
|
||||||
|
Expires: time.Unix(0, 0),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req request.LoginRequest
|
||||||
|
|
||||||
|
if err := httputil.BindJson(r, &req); err != nil {
|
||||||
|
httputil.Fail(w, errs.ErrInvalidCredentials)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.authService.Login(r.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: h.cfg.JWTConfig.CookieName,
|
||||||
|
Value: res.RefreshToken,
|
||||||
|
Path: "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
Secure: true,
|
||||||
|
SameSite: http.SameSiteNoneMode,
|
||||||
|
Expires: res.RefreshTokenExp,
|
||||||
|
})
|
||||||
|
|
||||||
|
httputil.Ok(w, map[string]any{
|
||||||
|
"access_token": res.AccessToken,
|
||||||
|
"access_token_exp": res.AccessTokenExp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
||||||
|
cookie, err := r.Cookie(h.cfg.JWTConfig.CookieName)
|
||||||
|
if err == nil {
|
||||||
|
_ = h.authService.Logout(r.Context(), cookie.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.clearRefreshTokenCookie(w)
|
||||||
|
httputil.Ok(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AuthHandler) RefreshToken(w http.ResponseWriter, r *http.Request) {
|
||||||
|
cookie, err := r.Cookie(h.cfg.JWTConfig.CookieName)
|
||||||
|
if err != nil {
|
||||||
|
h.clearRefreshTokenCookie(w)
|
||||||
|
httputil.Fail(w, errs.ErrInvalidRefreshToken)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.authService.RefreshToken(r.Context(), cookie.Value)
|
||||||
|
if err != nil {
|
||||||
|
// 用户被禁用 调用logout
|
||||||
|
_ = h.authService.Logout(r.Context(), cookie.Value)
|
||||||
|
h.clearRefreshTokenCookie(w)
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httputil.Ok(w, map[string]any{
|
||||||
|
"access_token": res.AccessToken,
|
||||||
|
"access_token_exp": res.AccessTokenExp,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ var Module = fx.Module("handlers",
|
|||||||
NewSysFileHandler,
|
NewSysFileHandler,
|
||||||
NewSysPostHandler,
|
NewSysPostHandler,
|
||||||
NewCategoryHandler,
|
NewCategoryHandler,
|
||||||
|
NewAuthHandler,
|
||||||
|
NewTagHandler,
|
||||||
|
|
||||||
NewPostHandler,
|
NewPostHandler,
|
||||||
NewWebHandler,
|
NewWebHandler,
|
||||||
|
|||||||
@@ -81,3 +81,13 @@ func (h *PostHandler) ListArchives(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
httputil.Ok(w, list)
|
httputil.Ok(w, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *PostHandler) ListPostTags(w http.ResponseWriter, r *http.Request) {
|
||||||
|
list, err := h.postService.ListPostTags(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httputil.Ok(w, list)
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,13 @@ func NewSysApiHandler(apiService *service.SysApiService) *SysApiHandler {
|
|||||||
func (h *SysApiHandler) ListPage(w http.ResponseWriter, r *http.Request) {
|
func (h *SysApiHandler) ListPage(w http.ResponseWriter, r *http.Request) {
|
||||||
pagination := httputil.Pagination(r)
|
pagination := httputil.Pagination(r)
|
||||||
|
|
||||||
list, total, err := h.apiService.ListPage(r.Context(), pagination)
|
params := request.SearchSysApiParams{
|
||||||
|
Pagination: *pagination,
|
||||||
|
GroupName: r.URL.Query().Get("group_name"),
|
||||||
|
Method: r.URL.Query().Get("method"),
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := h.apiService.ListPage(r.Context(), params)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httputil.Fail(w, err)
|
httputil.Fail(w, err)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
"server/internal/service"
|
"server/internal/service"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysUserHandler struct {
|
type SysUserHandler struct {
|
||||||
@@ -21,74 +20,15 @@ func NewSysUserHandler(userService *service.SysUserService) *SysUserHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SysUserHandler) Login(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var req request.LoginRequest
|
|
||||||
|
|
||||||
if err := httputil.BindJson(r, &req); err != nil {
|
|
||||||
httputil.Fail(w, errs.ErrInvalidCredentials)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := h.userService.Login(r.Context(), req)
|
|
||||||
if err != nil {
|
|
||||||
httputil.Fail(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
|
||||||
Name: "refresh_token",
|
|
||||||
Value: res.RefreshToken,
|
|
||||||
Path: "/",
|
|
||||||
HttpOnly: true,
|
|
||||||
Secure: true,
|
|
||||||
SameSite: http.SameSiteNoneMode,
|
|
||||||
Expires: res.RefreshTokenExp,
|
|
||||||
})
|
|
||||||
|
|
||||||
httputil.Ok(w, map[string]any{
|
|
||||||
"access_token": res.AccessToken,
|
|
||||||
"access_token_exp": res.AccessTokenExp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SysUserHandler) Logout(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
|
||||||
Name: "refresh_token",
|
|
||||||
Value: "",
|
|
||||||
Path: "/",
|
|
||||||
HttpOnly: true,
|
|
||||||
Secure: true,
|
|
||||||
SameSite: http.SameSiteNoneMode,
|
|
||||||
MaxAge: -1,
|
|
||||||
Expires: time.Unix(0, 0),
|
|
||||||
})
|
|
||||||
|
|
||||||
httputil.Ok(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SysUserHandler) RefreshToken(w http.ResponseWriter, r *http.Request) {
|
|
||||||
cookie, err := r.Cookie("refresh_token")
|
|
||||||
if err != nil {
|
|
||||||
httputil.Fail(w, errs.ErrInvalidRefreshToken)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := h.userService.RefreshToken(r.Context(), cookie.Value)
|
|
||||||
if err != nil {
|
|
||||||
httputil.Fail(w, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httputil.Ok(w, map[string]any{
|
|
||||||
"access_token": res.AccessToken,
|
|
||||||
"access_token_exp": res.AccessTokenExp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SysUserHandler) ListPage(w http.ResponseWriter, r *http.Request) {
|
func (h *SysUserHandler) ListPage(w http.ResponseWriter, r *http.Request) {
|
||||||
pagination := httputil.Pagination(r)
|
pagination := httputil.Pagination(r)
|
||||||
|
|
||||||
list, total, err := h.userService.ListPage(r.Context(), pagination)
|
params := request.SearchSysUserParams{
|
||||||
|
Pagination: *pagination,
|
||||||
|
Username: r.URL.Query().Get("username"),
|
||||||
|
}
|
||||||
|
|
||||||
|
list, total, err := h.userService.ListPage(r.Context(), params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httputil.Fail(w, err)
|
httputil.Fail(w, err)
|
||||||
return
|
return
|
||||||
|
|||||||
99
internal/handler/tag.go
Normal file
99
internal/handler/tag.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"server/internal/model/common"
|
||||||
|
"server/internal/model/request"
|
||||||
|
"server/internal/pkg/httputil"
|
||||||
|
"server/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TagHandler struct {
|
||||||
|
tagService *service.TagService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTagHandler(tagService *service.TagService) *TagHandler {
|
||||||
|
return &TagHandler{
|
||||||
|
tagService: tagService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TagHandler) ListPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
pagination := httputil.Pagination(r)
|
||||||
|
list, total, err := h.tagService.ListPage(r.Context(), pagination)
|
||||||
|
if err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := common.PageResponse{
|
||||||
|
Page: pagination.Page,
|
||||||
|
PageSize: pagination.PageSize,
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
}
|
||||||
|
httputil.OkWithPage(w, &resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TagHandler) ListAll(w http.ResponseWriter, r *http.Request) {
|
||||||
|
list, err := h.tagService.ListAll(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httputil.Ok(w, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TagHandler) Create(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req request.CreateTagRequest
|
||||||
|
|
||||||
|
if err := httputil.BindJson(r, &req); err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.tagService.Create(r.Context(), req); err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httputil.Ok(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TagHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, err := httputil.URLParamInt32(r, "id")
|
||||||
|
if err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req request.UpdateTagRequest
|
||||||
|
|
||||||
|
if err = httputil.BindJson(r, &req); err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.tagService.Update(r.Context(), id, req); err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httputil.Ok(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TagHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, err := httputil.URLParamInt32(r, "id")
|
||||||
|
if err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.tagService.Delete(r.Context(), id); err != nil {
|
||||||
|
httputil.Fail(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httputil.Ok(w)
|
||||||
|
}
|
||||||
@@ -3,8 +3,10 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/pkg/cache"
|
"server/internal/pkg/cache"
|
||||||
|
"server/internal/pkg/cache/cachekey"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -17,20 +19,21 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AuthMiddleware struct {
|
type AuthMiddleware struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
cache *cache.Caches
|
cache *cache.Caches
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthMiddleware(queries *db.Queries, cache *cache.Caches) *AuthMiddleware {
|
func NewAuthMiddleware(store *db.Store, cache *cache.Caches) *AuthMiddleware {
|
||||||
return &AuthMiddleware{
|
return &AuthMiddleware{
|
||||||
cache: cache,
|
cache: cache,
|
||||||
queries: queries,
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AuthMiddleware) Middleware(next http.Handler) http.Handler {
|
func (m *AuthMiddleware) Middleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
var err error
|
||||||
|
|
||||||
claims, ok := GetClaims(ctx)
|
claims, ok := GetClaims(ctx)
|
||||||
if !ok || claims.UserID == 0 {
|
if !ok || claims.UserID == 0 {
|
||||||
@@ -47,7 +50,8 @@ func (m *AuthMiddleware) Middleware(next http.Handler) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPermission, err := userHasApiPermission(ctx, r, m.queries, claims.UserID, m.cache)
|
// 不是管理员 判断api权限
|
||||||
|
hasPermission, err := userHasApiPermission(ctx, r, m.store, claims.UserID, m.cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httputil.Fail(w, err)
|
httputil.Fail(w, err)
|
||||||
return
|
return
|
||||||
@@ -70,22 +74,21 @@ func userIsAdmin(uid int32) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func userHasApiPermission(ctx context.Context, r *http.Request, queries *db.Queries, uid int32, cache *cache.Caches) (bool, error) {
|
func userHasApiPermission(ctx context.Context, r *http.Request, store *db.Store, uid int32, c *cache.Caches) (bool, error) {
|
||||||
var (
|
var (
|
||||||
apis []db.GetSysUserApisRow
|
apis []sqlc.GetSysUserApisRow
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
apis, ok := cache.SysUserApisCache.GetIfPresent(uid)
|
// 先从缓存中获取api数据
|
||||||
|
k := cachekey.UserApiPermissions(uid)
|
||||||
if !ok {
|
apis, err = cache.GetOrSetJSON[[]sqlc.GetSysUserApisRow](ctx, c, k, 0, func() ([]sqlc.GetSysUserApisRow, error) {
|
||||||
apis, err = queries.GetSysUserApis(ctx, uid)
|
return store.GetSysUserApis(ctx, uid)
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
cache.SysUserApisCache.Set(uid, apis)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPath := chi.RouteContext(r.Context()).RoutePattern()
|
requestPath := chi.RouteContext(r.Context()).RoutePattern()
|
||||||
requestPath = strings.TrimPrefix(requestPath, "/api")
|
requestPath = strings.TrimPrefix(requestPath, "/api")
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"net/http"
|
"net/http"
|
||||||
"server/internal/config"
|
"server/internal/config"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
@@ -71,46 +73,6 @@ func (m *JWTMiddleware) ParseToken(tokenStr string) (*Claims, error) {
|
|||||||
return claims, nil
|
return claims, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRefreshToken 解析RefreshToken
|
|
||||||
func (m *JWTMiddleware) ParseRefreshToken(tokenStr string) (*RefreshClaims, error) {
|
|
||||||
token, err := jwt.ParseWithClaims(
|
|
||||||
tokenStr,
|
|
||||||
&RefreshClaims{},
|
|
||||||
func(token *jwt.Token) (interface{}, error) {
|
|
||||||
return []byte(m.cfg.Secret), nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// Token 已过期
|
|
||||||
if errors.Is(err, jwt.ErrTokenExpired) {
|
|
||||||
return nil, errs.ErrExpiredRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
// 签名错误、格式错误、非法 Token
|
|
||||||
return nil, errs.ErrInvalidRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
if !token.Valid {
|
|
||||||
return nil, errs.ErrInvalidRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
claims, ok := token.Claims.(*RefreshClaims)
|
|
||||||
if !ok {
|
|
||||||
return nil, errs.ErrInvalidRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
if claims.UserID == 0 {
|
|
||||||
return nil, errs.ErrInvalidRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
if claims.Type != RefreshTokenType {
|
|
||||||
return nil, errs.ErrInvalidRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
return claims, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *JWTMiddleware) Middleware(next http.Handler) http.Handler {
|
func (m *JWTMiddleware) Middleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
auth := r.Header.Get("Authorization")
|
auth := r.Header.Get("Authorization")
|
||||||
@@ -161,24 +123,19 @@ func (m *JWTMiddleware) GenerateAccessToken(userID int32) (string, time.Time, er
|
|||||||
return token, expiresAt, nil
|
return token, expiresAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *JWTMiddleware) GenerateRefreshToken(userID int32) (string, time.Time, error) {
|
func (m *JWTMiddleware) GenerateRefreshToken() (string, error) {
|
||||||
now := time.Now()
|
b := make([]byte, 32)
|
||||||
expiresAt := now.Add(m.cfg.RefreshExpire)
|
|
||||||
|
|
||||||
refreshClaims := RefreshClaims{
|
_, err := rand.Read(b)
|
||||||
UserID: userID,
|
|
||||||
Type: RefreshTokenType,
|
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
|
||||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
|
||||||
IssuedAt: jwt.NewNumericDate(now),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)
|
|
||||||
token, err := refreshToken.SignedString([]byte(m.cfg.Secret))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", time.Time{}, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return token, expiresAt, nil
|
return hex.EncodeToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashRefreshToken 哈希token 存储至redis
|
||||||
|
func (m *JWTMiddleware) HashRefreshToken(token string) string {
|
||||||
|
hash := sha256.Sum256([]byte(token))
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
}
|
}
|
||||||
|
|||||||
9
internal/model/auth/jwt.go
Normal file
9
internal/model/auth/jwt.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type RefreshTokenRecord struct {
|
||||||
|
UserID int32 `json:"user_id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@ package request
|
|||||||
type CreateCategoryRequest struct {
|
type CreateCategoryRequest struct {
|
||||||
Name string `json:"name" validate:"required,min=1"`
|
Name string `json:"name" validate:"required,min=1"`
|
||||||
Code string `json:"code" validate:"required,min=1"`
|
Code string `json:"code" validate:"required,min=1"`
|
||||||
|
Sort *int32 `json:"sort" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateCategoryRequest struct {
|
type UpdateCategoryRequest struct {
|
||||||
Name *string `json:"name" validate:"omitempty,min=1"`
|
Name *string `json:"name" validate:"omitempty,min=1"`
|
||||||
Code *string `json:"code" validate:"omitempty,min=1"`
|
Code *string `json:"code" validate:"omitempty,min=1"`
|
||||||
|
Sort *int32 `json:"sort" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
type CreatePostRequest struct {
|
type CreatePostRequest struct {
|
||||||
Title string `json:"title" validate:"required,min=1"`
|
Title string `json:"title" validate:"required,min=1"`
|
||||||
CoverID *int32 `json:"cover_id" validate:"required,min=1"`
|
CoverID int32 `json:"cover_id" validate:"required,min=1"`
|
||||||
Slug string `json:"slug" validate:"required,min=0"`
|
Slug string `json:"slug" validate:"required,min=0"`
|
||||||
Content string `json:"content" validate:"required,min=0"`
|
Content string `json:"content" validate:"required,min=0"`
|
||||||
Summary string `json:"summary" validate:"required,min=0"`
|
Summary string `json:"summary" validate:"required,min=0"`
|
||||||
@@ -14,6 +14,7 @@ type CreatePostRequest struct {
|
|||||||
Sort *int32 `json:"sort" validate:"required,min=0"`
|
Sort *int32 `json:"sort" validate:"required,min=0"`
|
||||||
PublishedAt time.Time `json:"published_at" validate:"required"`
|
PublishedAt time.Time `json:"published_at" validate:"required"`
|
||||||
CategoryID *int32 `json:"category_id" validate:"required,min=1"`
|
CategoryID *int32 `json:"category_id" validate:"required,min=1"`
|
||||||
|
Tags []int32 `json:"tags" validate:"omitempty,dive,min=1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdatePostRequest struct {
|
type UpdatePostRequest struct {
|
||||||
@@ -27,4 +28,5 @@ type UpdatePostRequest struct {
|
|||||||
Sort *int32 `json:"sort" validate:"omitempty,min=0"`
|
Sort *int32 `json:"sort" validate:"omitempty,min=0"`
|
||||||
PublishedAt *time.Time `json:"published_at" validate:"omitempty"`
|
PublishedAt *time.Time `json:"published_at" validate:"omitempty"`
|
||||||
CategoryID *int32 `json:"category_id" validate:"required,min=1"`
|
CategoryID *int32 `json:"category_id" validate:"required,min=1"`
|
||||||
|
Tags []int32 `json:"tags" validate:"omitempty,dive,min=1"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package request
|
package request
|
||||||
|
|
||||||
|
import "server/internal/model/common"
|
||||||
|
|
||||||
type CreateSysApiRequest struct {
|
type CreateSysApiRequest struct {
|
||||||
Name string `json:"name" validate:"required,min=1,max=100"`
|
Name string `json:"name" validate:"required,min=1,max=100"`
|
||||||
GroupName string `json:"group_name" validate:"required,min=1,max=100"`
|
GroupName string `json:"group_name" validate:"required,min=1,max=100"`
|
||||||
@@ -15,3 +17,9 @@ type UpdateSysApiRequest struct {
|
|||||||
Method string `json:"method" validate:"required,oneof=GET POST PUT PATCH DELETE"`
|
Method string `json:"method" validate:"required,oneof=GET POST PUT PATCH DELETE"`
|
||||||
Sort *int32 `json:"sort" validate:"required,min=0"`
|
Sort *int32 `json:"sort" validate:"required,min=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchSysApiParams struct {
|
||||||
|
common.Pagination
|
||||||
|
Method string `json:"method" form:"method" validate:"omitempty,oneof=GET POST PUT PATCH DELETE"`
|
||||||
|
GroupName string `json:"group_name" form:"group_name" validate:"omitempty,max=100"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
type CreateSysMenuRequest struct {
|
type CreateSysMenuRequest struct {
|
||||||
Name string `json:"name" validate:"required,min=1,max=100"`
|
Name string `json:"name" validate:"required,min=1,max=100"`
|
||||||
Path string `json:"path" validate:"required,min=1,max=100"`
|
Path *string `json:"path" validate:"omitempty,max=100"`
|
||||||
Type *int16 `json:"type" validate:"required,oneof=0 1 2"`
|
Type *int16 `json:"type" validate:"required,oneof=0 1 2"`
|
||||||
Component *string `json:"component" validate:"omitempty"`
|
Component *string `json:"component" validate:"omitempty"`
|
||||||
Hidden *bool `json:"hidden" validate:"omitempty"`
|
Hidden *bool `json:"hidden" validate:"omitempty"`
|
||||||
@@ -19,13 +19,13 @@ type CreateSysMenuRequest struct {
|
|||||||
|
|
||||||
type UpdateSysMenuRequest struct {
|
type UpdateSysMenuRequest struct {
|
||||||
Name *string `json:"name" validate:"omitempty,min=1,max=100"`
|
Name *string `json:"name" validate:"omitempty,min=1,max=100"`
|
||||||
Path *string `json:"path" validate:"omitempty,min=1,max=100"`
|
Path *string `json:"path" validate:"omitempty,max=100"`
|
||||||
Type *int16 `json:"type" validate:"omitempty,oneof=0 1 2"`
|
Type *int16 `json:"type" validate:"omitempty,oneof=0 1 2"`
|
||||||
Component *string `json:"component" validate:"omitempty"`
|
Component *string `json:"component" validate:"omitempty"`
|
||||||
Hidden *bool `json:"hidden" validate:"omitempty"`
|
Hidden *bool `json:"hidden" validate:"omitempty"`
|
||||||
Sort *int32 `json:"sort" validate:"omitempty,min=0"`
|
Sort *int32 `json:"sort" validate:"omitempty,min=0"`
|
||||||
Status *int16 `json:"status" validate:"omitempty,oneof=0 1"`
|
Status *int16 `json:"status" validate:"omitempty,oneof=0 1"`
|
||||||
ParentID validator.NullInt32 `json:"parent_id" validate:"omitempty"`
|
ParentID validator.NullInt32 `json:"parent_id" validate:"omitempty"`
|
||||||
Icon *int32 `json:"icon" validate:"omitempty"`
|
Icon validator.NullInt32 `json:"icon" validate:"omitempty"`
|
||||||
PermissionCode *string `json:"permission_code" validate:"omitempty,min=1,max=100"`
|
PermissionCode *string `json:"permission_code" validate:"omitempty,min=1,max=100"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
package request
|
|
||||||
@@ -3,10 +3,12 @@ package request
|
|||||||
type CreateSysRoleRequest struct {
|
type CreateSysRoleRequest struct {
|
||||||
Name string `json:"name" validate:"required,min=1,max=100"`
|
Name string `json:"name" validate:"required,min=1,max=100"`
|
||||||
Code string `json:"code" validate:"required,max=100"`
|
Code string `json:"code" validate:"required,max=100"`
|
||||||
|
Status *int16 `json:"status" validate:"required,oneof=0 1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateSysRoleRequest struct {
|
type UpdateSysRoleRequest struct {
|
||||||
Name *string `json:"name" validate:"min=1,max=100"`
|
Name *string `json:"name" validate:"min=1,max=100"`
|
||||||
|
Status *int16 `json:"status" validate:"omitempty,oneof=0 1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetSysRoleMenusRequest struct {
|
type SetSysRoleMenusRequest struct {
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
package request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"server/internal/model/common"
|
||||||
"server/internal/pkg/validator"
|
"server/internal/pkg/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateSysUserRequest struct {
|
type CreateSysUserRequest struct {
|
||||||
Username string `json:"username" validate:"required"`
|
Username string `json:"username" validate:"required,min=1,max=50"`
|
||||||
Account string `json:"account" validate:"required,min=5,max=100"`
|
Account string `json:"account" validate:"required,min=5,max=100"`
|
||||||
Password string `json:"password" validate:"required,min=6,max=255"`
|
Password string `json:"password" validate:"required,min=6,max=255"`
|
||||||
AvatarID *int32 `json:"avatar_id" validate:"omitempty"`
|
AvatarID *int32 `json:"avatar_id" validate:"omitempty"`
|
||||||
|
Status *int16 `json:"status" validate:"oneof=0 1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateSysUserRequest struct {
|
type UpdateSysUserRequest struct {
|
||||||
Username *string `json:"username" validate:"omitempty,min=0,max=50"`
|
Username *string `json:"username" validate:"omitempty,min=1,max=50"`
|
||||||
AvatarID validator.NullInt32 `json:"avatar_id" validate:"omitempty"`
|
AvatarID validator.NullInt32 `json:"avatar_id" validate:"omitempty"`
|
||||||
|
Status *int16 `json:"status" validate:"omitempty,oneof=0 1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateSysUserPassword struct {
|
type UpdateSysUserPassword struct {
|
||||||
@@ -28,3 +31,8 @@ type LoginRequest struct {
|
|||||||
Account string `json:"account" validate:"required,min=5,max=100"`
|
Account string `json:"account" validate:"required,min=5,max=100"`
|
||||||
Password string `json:"password" validate:"required,min=6,max=255"`
|
Password string `json:"password" validate:"required,min=6,max=255"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchSysUserParams struct {
|
||||||
|
common.Pagination
|
||||||
|
Username string `json:"username" validate:"omitempty,max=50"`
|
||||||
|
}
|
||||||
|
|||||||
13
internal/model/request/tag.go
Normal file
13
internal/model/request/tag.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
type CreateTagRequest struct {
|
||||||
|
Name string `json:"name" validate:"required,min=1"`
|
||||||
|
Code string `json:"code" validate:"required,min=1"`
|
||||||
|
Sort *int32 `json:"sort" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateTagRequest struct {
|
||||||
|
Name *string `json:"name" validate:"omitempty,min=1"`
|
||||||
|
Code *string `json:"code" validate:"omitempty,min=1"`
|
||||||
|
Sort *int32 `json:"sort" validate:"omitempty"`
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package response
|
package response
|
||||||
|
|
||||||
import (
|
import (
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db/sqlc"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ToFiles(files []db.File) []db.File {
|
func ToFiles(files []sqlc.File) []sqlc.File {
|
||||||
result := make([]db.File, len(files))
|
result := make([]sqlc.File, len(files))
|
||||||
for i := range files {
|
for i := range files {
|
||||||
result[i] = files[i]
|
result[i] = files[i]
|
||||||
result[i].FilePath = httputil.BuildFileUrl(&files[i].FilePath)
|
result[i].FilePath = httputil.BuildFileUrl(&files[i].FilePath)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package response
|
package response
|
||||||
|
|
||||||
import (
|
import (
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db/sqlc"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -18,7 +18,8 @@ type SysUserInfo struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
AvatarUrl string `json:"avatar_url"`
|
AvatarUrl string `json:"avatar_url"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Menus []db.SysMenu `json:"menus"`
|
Menus []sqlc.SysMenu `json:"menus"`
|
||||||
|
Permissions []string `json:"permissions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginResponse struct {
|
type LoginResponse struct {
|
||||||
@@ -28,12 +29,19 @@ type LoginResponse struct {
|
|||||||
RefreshTokenExp time.Time `json:"refresh_token_exp"`
|
RefreshTokenExp time.Time `json:"refresh_token_exp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysUserInfo(user db.GetSysUserByIDRow, roles []db.SysRole, menus []db.SysMenu) *SysUserInfo {
|
func NewSysUserInfo(user sqlc.GetSysUserByIDRow, roles []sqlc.SysRole, menus []sqlc.SysMenu, p []*string) *SysUserInfo {
|
||||||
roleCodes := make([]string, len(roles))
|
roleCodes := make([]string, len(roles))
|
||||||
for i, role := range roles {
|
for i, role := range roles {
|
||||||
roleCodes[i] = role.Code
|
roleCodes[i] = role.Code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
permissions := make([]string, 0, len(p))
|
||||||
|
for _, code := range p {
|
||||||
|
if code != nil {
|
||||||
|
permissions = append(permissions, *code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &SysUserInfo{
|
return &SysUserInfo{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Account: user.Account,
|
Account: user.Account,
|
||||||
@@ -41,5 +49,6 @@ func NewSysUserInfo(user db.GetSysUserByIDRow, roles []db.SysRole, menus []db.Sy
|
|||||||
AvatarUrl: httputil.BuildFileUrl(user.AvatarUrl),
|
AvatarUrl: httputil.BuildFileUrl(user.AvatarUrl),
|
||||||
Roles: roleCodes,
|
Roles: roleCodes,
|
||||||
Menus: menus,
|
Menus: menus,
|
||||||
|
Permissions: permissions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
218
internal/pkg/cache/cache.go
vendored
218
internal/pkg/cache/cache.go
vendored
@@ -1,31 +1,219 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/internal/db/sqlc"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/maypok86/otter/v2"
|
"github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
|
"server/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Caches struct {
|
type Caches struct {
|
||||||
SysUserApisCache *otter.Cache[int32, []db.GetSysUserApisRow]
|
rdb *redis.Client
|
||||||
|
keyPrefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCaches() *Caches {
|
func NewCaches(rdb *redis.Client, cfg *config.Config) *Caches {
|
||||||
sysUserApisCache := otter.Must(&otter.Options[int32, []db.GetSysUserApisRow]{
|
|
||||||
MaximumSize: 1_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
return &Caches{
|
return &Caches{
|
||||||
SysUserApisCache: sysUserApisCache,
|
rdb: rdb,
|
||||||
|
keyPrefix: cfg.Redis.KeyPrefix,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearSysUserCache 清理单个用户缓存
|
func (c *Caches) key(key string) string {
|
||||||
func (c *Caches) ClearSysUserCache(userID int32) {
|
if c.keyPrefix == "" {
|
||||||
c.SysUserApisCache.Invalidate(userID)
|
return key
|
||||||
|
}
|
||||||
|
return c.keyPrefix + key
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearAllSysUserCache 清理所有用户缓存
|
func (c *Caches) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
|
||||||
func (c *Caches) ClearAllSysUserCache() {
|
return c.rdb.Set(ctx, c.key(key), value, expiration).Err()
|
||||||
c.SysUserApisCache.InvalidateAll()
|
}
|
||||||
|
|
||||||
|
func (c *Caches) Get(ctx context.Context, key string) (string, bool, error) {
|
||||||
|
val, err := c.rdb.Get(ctx, c.key(key)).Result()
|
||||||
|
if errors.Is(err, redis.Nil) {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return val, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Caches) SetJSON(ctx context.Context, key string, value any, expiration time.Duration) error {
|
||||||
|
b, err := json.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.rdb.Set(ctx, c.key(key), b, expiration).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetJSON[T any](ctx context.Context, c *Caches, key string) (*T, bool, error) {
|
||||||
|
val, err := c.rdb.Get(ctx, c.key(key)).Bytes()
|
||||||
|
if errors.Is(err, redis.Nil) {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result T
|
||||||
|
if err = json.Unmarshal(val, &result); err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return &result, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrSetJSON[T any](ctx context.Context, c *Caches, key string, expiration time.Duration, fetch func() (T, error)) (T, error) {
|
||||||
|
var zero T
|
||||||
|
|
||||||
|
cacheKey := c.key(key)
|
||||||
|
|
||||||
|
val, err := c.rdb.Get(ctx, cacheKey).Bytes()
|
||||||
|
if err == nil {
|
||||||
|
var result T
|
||||||
|
if err = json.Unmarshal(val, &result); err == nil {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !errors.Is(err, redis.Nil) {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := fetch()
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
_ = c.rdb.Set(ctx, cacheKey, b, expiration).Err()
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Caches) Del(ctx context.Context, keys ...string) error {
|
||||||
|
for i := range keys {
|
||||||
|
keys[i] = c.key(keys[i])
|
||||||
|
}
|
||||||
|
return c.rdb.Del(ctx, keys...).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Caches) Exists(ctx context.Context, key string) (bool, error) {
|
||||||
|
n, err := c.rdb.Exists(ctx, c.key(key)).Result()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return n > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAdd 向 Set 中添加成员
|
||||||
|
func (c *Caches) SAdd(ctx context.Context, key string, members ...any) error {
|
||||||
|
return c.rdb.SAdd(ctx, c.key(key), members...).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SMembers 获取 Set 中所有成员
|
||||||
|
func (c *Caches) SMembers(ctx context.Context, key string) ([]string, error) {
|
||||||
|
return c.rdb.SMembers(ctx, c.key(key)).Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelSetMembers 删除 Set 中记录的所有成员对应的缓存 key,并删除 Set 本身
|
||||||
|
func (c *Caches) DelSetMembers(ctx context.Context, key string) error {
|
||||||
|
members, err := c.SMembers(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := append(members, key)
|
||||||
|
return c.Del(ctx, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SRem 从 Set 中移除成员
|
||||||
|
func (c *Caches) SRem(ctx context.Context, key string, members ...any) error {
|
||||||
|
return c.rdb.SRem(ctx, c.key(key), members...).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIsMember 判断 member 是否在 Set 中
|
||||||
|
func (c *Caches) SIsMember(ctx context.Context, key string, member any) (bool, error) {
|
||||||
|
return c.rdb.SIsMember(ctx, c.key(key), member).Result()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MGet 批量获取多个 key 的值
|
||||||
|
func (c *Caches) MGet(ctx context.Context, keys ...string) (map[string]string, error) {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return map[string]string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
redisKeys := make([]string, len(keys))
|
||||||
|
for i, key := range keys {
|
||||||
|
redisKeys[i] = c.key(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
values, err := c.rdb.MGet(ctx, redisKeys...).Result()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]string, len(keys))
|
||||||
|
for i, value := range values {
|
||||||
|
if value == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
str, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result[keys[i]] = str
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelByPrefix 批量删除指定前缀的缓存键
|
||||||
|
func (c *Caches) DelByPrefix(ctx context.Context, prefix string) error {
|
||||||
|
prefix = c.key(prefix)
|
||||||
|
|
||||||
|
if !strings.HasSuffix(prefix, "*") {
|
||||||
|
prefix += "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
var cursor uint64
|
||||||
|
var keys []string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for {
|
||||||
|
keys, cursor, err = c.rdb.Scan(ctx, cursor, prefix, 100).Result()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(keys) > 0 {
|
||||||
|
if err = c.rdb.Del(ctx, keys...).Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Caches) Expire(ctx context.Context, key string, expiration time.Duration) error {
|
||||||
|
return c.rdb.Expire(ctx, c.keyPrefix+key, expiration).Err()
|
||||||
}
|
}
|
||||||
|
|||||||
25
internal/pkg/cache/cachekey/key.go
vendored
Normal file
25
internal/pkg/cache/cachekey/key.go
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package cachekey
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserApiPermissionsPattern = "user:api:permissions:*"
|
||||||
|
UserInfoPattern = "user:info:*"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UserApiPermissions(id int32) string {
|
||||||
|
return fmt.Sprintf("user:api:permissions:%d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserInfo(id int32) string {
|
||||||
|
return fmt.Sprintf("user:info:%d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthRefresh(hash string) string {
|
||||||
|
return fmt.Sprintf("auth:refresh:%s", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthRefreshUser 反向索引 用于定位refresh token来删除
|
||||||
|
func AuthRefreshUser(userID int32) string {
|
||||||
|
return fmt.Sprintf("auth:refresh:user:%d", userID)
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ const (
|
|||||||
SysPermissionsCodeKey = "sys_permissions_code_key"
|
SysPermissionsCodeKey = "sys_permissions_code_key"
|
||||||
SysApisMethodPathKey = "sys_apis_method_path_key"
|
SysApisMethodPathKey = "sys_apis_method_path_key"
|
||||||
CategoryCodeKey = "categories_code_key"
|
CategoryCodeKey = "categories_code_key"
|
||||||
|
TagCodeKey = "tags_code_key"
|
||||||
|
SysMenusPathUniqueIdx = "sys_menus_path_unique_idx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapRowsAffected(rows int64, err error, notFoundErr error) error {
|
func MapRowsAffected(rows int64, err error, notFoundErr error) error {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ func New(httpCode int, msg string) *AppError {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidCredentials = New(http.StatusUnauthorized, "用户名或密码错误")
|
ErrInvalidCredentials = New(http.StatusUnauthorized, "用户名或密码错误")
|
||||||
|
ErrUserDisabled = New(http.StatusForbidden, "用户已被禁用")
|
||||||
ErrUnauthenticated = New(http.StatusUnauthorized, "用户未登录或登录已失效")
|
ErrUnauthenticated = New(http.StatusUnauthorized, "用户未登录或登录已失效")
|
||||||
ErrUnauthorized = New(http.StatusUnauthorized, "认证失败:无法获取当前用户信息")
|
ErrUnauthorized = New(http.StatusUnauthorized, "认证失败:无法获取当前用户信息")
|
||||||
ErrInvalidToken = New(http.StatusUnauthorized, "登录凭证无效")
|
ErrInvalidToken = New(http.StatusUnauthorized, "登录凭证无效")
|
||||||
@@ -28,6 +29,7 @@ var (
|
|||||||
ErrExpiredRefreshToken = New(http.StatusBadRequest, "invalid_grant")
|
ErrExpiredRefreshToken = New(http.StatusBadRequest, "invalid_grant")
|
||||||
ErrUserNotFound = New(http.StatusNotFound, "用户数据不存在")
|
ErrUserNotFound = New(http.StatusNotFound, "用户数据不存在")
|
||||||
ErrCategoryNotFound = New(http.StatusNotFound, "分类数据不存在")
|
ErrCategoryNotFound = New(http.StatusNotFound, "分类数据不存在")
|
||||||
|
ErrTagNotFound = New(http.StatusNotFound, "标签数据不存在")
|
||||||
ErrSysApiNotFound = New(http.StatusNotFound, "接口数据不存在")
|
ErrSysApiNotFound = New(http.StatusNotFound, "接口数据不存在")
|
||||||
ErrPostNotFound = New(http.StatusNotFound, "文章数据不存在")
|
ErrPostNotFound = New(http.StatusNotFound, "文章数据不存在")
|
||||||
ErrSysMenuNotFound = New(http.StatusNotFound, "菜单数据不存在")
|
ErrSysMenuNotFound = New(http.StatusNotFound, "菜单数据不存在")
|
||||||
@@ -40,9 +42,11 @@ var (
|
|||||||
ErrEmptyBody = New(http.StatusBadRequest, "请求内容(body)不能为空")
|
ErrEmptyBody = New(http.StatusBadRequest, "请求内容(body)不能为空")
|
||||||
ErrInvalidJSON = New(http.StatusBadRequest, "请求数据格式错误")
|
ErrInvalidJSON = New(http.StatusBadRequest, "请求数据格式错误")
|
||||||
ErrAccountAlreadyExists = New(http.StatusBadRequest, "账号已存在")
|
ErrAccountAlreadyExists = New(http.StatusBadRequest, "账号已存在")
|
||||||
ErrCodeAlreadyExists = New(http.StatusBadRequest, "角色编码已存在")
|
ErrCodeAlreadyExists = New(http.StatusBadRequest, "角色编码不允许重复")
|
||||||
ErrSlugAlreadyExists = New(http.StatusBadRequest, "slug已存在")
|
ErrSlugAlreadyExists = New(http.StatusBadRequest, "slug不允许重复")
|
||||||
ErrPermissionCodeAlreadyExists = New(http.StatusBadRequest, "权限编码已存在")
|
ErrPermissionCodeAlreadyExists = New(http.StatusBadRequest, "权限编码不允许重复")
|
||||||
ErrCategoryCodeAlreadyExists = New(http.StatusBadRequest, "分类编码已存在")
|
ErrCategoryCodeAlreadyExists = New(http.StatusBadRequest, "分类编码不允许重复")
|
||||||
ErrSysApiMethodPathAlreadyExists = New(http.StatusBadRequest, "接口方法(method)路径(path)已存在")
|
ErrTagCodeAlreadyExists = New(http.StatusBadRequest, "标签编码不允许重复")
|
||||||
|
ErrSysMenusPathUniqueIdx = New(http.StatusBadRequest, "菜单路径不允许重复")
|
||||||
|
ErrSysApiMethodPathAlreadyExists = New(http.StatusBadRequest, "接口方法(method)路径(path)不允许重复")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ func SetupPostRoutes(r chi.Router, h *handler.PostHandler) {
|
|||||||
r.Get("/post/{slug}", h.GetPost)
|
r.Get("/post/{slug}", h.GetPost)
|
||||||
r.Get("/category/stats", h.ListCategoryStats)
|
r.Get("/category/stats", h.ListCategoryStats)
|
||||||
r.Get("/post/archive", h.ListArchives)
|
r.Get("/post/archive", h.ListArchives)
|
||||||
|
r.Get("/post/tags", h.ListPostTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupWebRoutes(r chi.Router, h *handler.WebHandler) {
|
func SetupWebRoutes(r chi.Router, h *handler.WebHandler) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type Handlers struct {
|
|||||||
AuthMiddleware *middleware.AuthMiddleware
|
AuthMiddleware *middleware.AuthMiddleware
|
||||||
LoggerMiddleware *middleware.LoggerMiddleware
|
LoggerMiddleware *middleware.LoggerMiddleware
|
||||||
// system
|
// system
|
||||||
|
AuthHandler *handler.AuthHandler
|
||||||
SysUserHandler *handler.SysUserHandler
|
SysUserHandler *handler.SysUserHandler
|
||||||
SysRoleHandler *handler.SysRoleHandler
|
SysRoleHandler *handler.SysRoleHandler
|
||||||
SysMenuHandler *handler.SysMenuHandler
|
SysMenuHandler *handler.SysMenuHandler
|
||||||
@@ -26,6 +27,7 @@ type Handlers struct {
|
|||||||
SysFileHandler *handler.SysFileHandler
|
SysFileHandler *handler.SysFileHandler
|
||||||
SysPostHandler *handler.SysPostHandler
|
SysPostHandler *handler.SysPostHandler
|
||||||
CategoryHandler *handler.CategoryHandler
|
CategoryHandler *handler.CategoryHandler
|
||||||
|
TagHandler *handler.TagHandler
|
||||||
// web
|
// web
|
||||||
PostHandler *handler.PostHandler
|
PostHandler *handler.PostHandler
|
||||||
WebHandler *handler.WebHandler
|
WebHandler *handler.WebHandler
|
||||||
@@ -50,9 +52,9 @@ func NewRouter(m Handlers) *chi.Mux {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.Route("/api/admin", func(r chi.Router) {
|
r.Route("/api/admin", func(r chi.Router) {
|
||||||
r.Post("/auth/login", m.SysUserHandler.Login)
|
r.Post("/auth/login", m.AuthHandler.Login)
|
||||||
r.Post("/auth/refresh-token", m.SysUserHandler.RefreshToken)
|
r.Post("/auth/refresh-token", m.AuthHandler.RefreshToken)
|
||||||
r.Post("/auth/logout", m.SysUserHandler.Logout)
|
r.Post("/auth/logout", m.AuthHandler.Logout)
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
r.Use(m.JwtMiddleware.Middleware)
|
r.Use(m.JwtMiddleware.Middleware)
|
||||||
@@ -65,6 +67,7 @@ func NewRouter(m Handlers) *chi.Mux {
|
|||||||
SetupSysFileRoutes(r, m.SysFileHandler)
|
SetupSysFileRoutes(r, m.SysFileHandler)
|
||||||
SetupCategoryRoutes(r, m.CategoryHandler)
|
SetupCategoryRoutes(r, m.CategoryHandler)
|
||||||
SetupSysPostRoutes(r, m.SysPostHandler)
|
SetupSysPostRoutes(r, m.SysPostHandler)
|
||||||
|
SetupTagRoutes(r, m.TagHandler)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetupSysUserRoutes 8
|
||||||
func SetupSysUserRoutes(r chi.Router, h *handler.SysUserHandler) {
|
func SetupSysUserRoutes(r chi.Router, h *handler.SysUserHandler) {
|
||||||
r.Get("/user", h.ListPage)
|
r.Get("/user", h.ListPage)
|
||||||
r.Get("/user/info", h.GetUserInfo)
|
r.Get("/user/info", h.GetUserInfo)
|
||||||
@@ -17,6 +18,7 @@ func SetupSysUserRoutes(r chi.Router, h *handler.SysUserHandler) {
|
|||||||
r.Delete("/user/{id}", h.Delete)
|
r.Delete("/user/{id}", h.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupSysRoleRoutes 9
|
||||||
func SetupSysRoleRoutes(r chi.Router, h *handler.SysRoleHandler) {
|
func SetupSysRoleRoutes(r chi.Router, h *handler.SysRoleHandler) {
|
||||||
r.Get("/role", h.ListPage)
|
r.Get("/role", h.ListPage)
|
||||||
r.Get("/role/{id}/menus", h.GetRoleMenus)
|
r.Get("/role/{id}/menus", h.GetRoleMenus)
|
||||||
@@ -29,6 +31,7 @@ func SetupSysRoleRoutes(r chi.Router, h *handler.SysRoleHandler) {
|
|||||||
r.Delete("/role/{id}", h.Delete)
|
r.Delete("/role/{id}", h.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupSysMenuRoutes 5
|
||||||
func SetupSysMenuRoutes(r chi.Router, h *handler.SysMenuHandler) {
|
func SetupSysMenuRoutes(r chi.Router, h *handler.SysMenuHandler) {
|
||||||
r.Get("/menu", h.ListPage)
|
r.Get("/menu", h.ListPage)
|
||||||
r.Get("/menu/all", h.GetMenus)
|
r.Get("/menu/all", h.GetMenus)
|
||||||
@@ -37,6 +40,7 @@ func SetupSysMenuRoutes(r chi.Router, h *handler.SysMenuHandler) {
|
|||||||
r.Delete("/menu/{id}", h.Delete)
|
r.Delete("/menu/{id}", h.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupSysApiRoutes 6
|
||||||
func SetupSysApiRoutes(r chi.Router, h *handler.SysApiHandler) {
|
func SetupSysApiRoutes(r chi.Router, h *handler.SysApiHandler) {
|
||||||
r.Get("/api", h.ListPage)
|
r.Get("/api", h.ListPage)
|
||||||
r.Get("/api/all", h.GetAllSysApis)
|
r.Get("/api/all", h.GetAllSysApis)
|
||||||
@@ -46,11 +50,13 @@ func SetupSysApiRoutes(r chi.Router, h *handler.SysApiHandler) {
|
|||||||
r.Delete("/api/{id}", h.Delete)
|
r.Delete("/api/{id}", h.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupSysFileRoutes 2
|
||||||
func SetupSysFileRoutes(r chi.Router, h *handler.SysFileHandler) {
|
func SetupSysFileRoutes(r chi.Router, h *handler.SysFileHandler) {
|
||||||
r.Get("/file", h.ListPage)
|
r.Get("/file", h.ListPage)
|
||||||
r.Post("/file", h.Upload)
|
r.Post("/file", h.Upload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupSysPostRoutes 5
|
||||||
func SetupSysPostRoutes(r chi.Router, h *handler.SysPostHandler) {
|
func SetupSysPostRoutes(r chi.Router, h *handler.SysPostHandler) {
|
||||||
r.Get("/post", h.ListPage)
|
r.Get("/post", h.ListPage)
|
||||||
r.Get("/post/{id}", h.GetPostById)
|
r.Get("/post/{id}", h.GetPostById)
|
||||||
@@ -59,6 +65,7 @@ func SetupSysPostRoutes(r chi.Router, h *handler.SysPostHandler) {
|
|||||||
r.Delete("/post/{id}", h.Delete)
|
r.Delete("/post/{id}", h.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupCategoryRoutes 5
|
||||||
func SetupCategoryRoutes(r chi.Router, h *handler.CategoryHandler) {
|
func SetupCategoryRoutes(r chi.Router, h *handler.CategoryHandler) {
|
||||||
r.Get("/category", h.ListPage)
|
r.Get("/category", h.ListPage)
|
||||||
r.Get("/category/all", h.ListAll)
|
r.Get("/category/all", h.ListAll)
|
||||||
@@ -66,3 +73,12 @@ func SetupCategoryRoutes(r chi.Router, h *handler.CategoryHandler) {
|
|||||||
r.Patch("/category/{id}", h.Update)
|
r.Patch("/category/{id}", h.Update)
|
||||||
r.Delete("/category/{id}", h.Delete)
|
r.Delete("/category/{id}", h.Delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupTagRoutes 5
|
||||||
|
func SetupTagRoutes(r chi.Router, h *handler.TagHandler) {
|
||||||
|
r.Get("/tag", h.ListPage)
|
||||||
|
r.Get("/tag/all", h.ListAll)
|
||||||
|
r.Post("/tag", h.Create)
|
||||||
|
r.Patch("/tag/{id}", h.Update)
|
||||||
|
r.Delete("/tag/{id}", h.Delete)
|
||||||
|
}
|
||||||
|
|||||||
180
internal/service/auth.go
Normal file
180
internal/service/auth.go
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"server/internal/config"
|
||||||
|
"server/internal/db"
|
||||||
|
"server/internal/middleware"
|
||||||
|
"server/internal/model/auth"
|
||||||
|
"server/internal/model/request"
|
||||||
|
"server/internal/model/response"
|
||||||
|
"server/internal/pkg/cache"
|
||||||
|
"server/internal/pkg/cache/cachekey"
|
||||||
|
"server/internal/pkg/errs"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthService struct {
|
||||||
|
jwt *middleware.JWTMiddleware
|
||||||
|
cache *cache.Caches
|
||||||
|
store *db.Store
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthService(jwt *middleware.JWTMiddleware, cache *cache.Caches, store *db.Store, cfg *config.Config) *AuthService {
|
||||||
|
return &AuthService{
|
||||||
|
jwt: jwt,
|
||||||
|
cache: cache,
|
||||||
|
store: store,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generatePasswordHash 生成密码哈希
|
||||||
|
func generatePasswordHash(password string) (string, error) {
|
||||||
|
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(hashed), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// comparePasswordHash 比较密码哈希
|
||||||
|
func comparePasswordHash(passwordHash, inputPassword string) error {
|
||||||
|
return bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(inputPassword))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) Login(ctx context.Context, req request.LoginRequest) (*response.LoginResponse, error) {
|
||||||
|
user, err := s.store.GetSysUserByAccount(ctx, req.Account)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrInvalidCredentials
|
||||||
|
}
|
||||||
|
|
||||||
|
// 此处判断如果用户id不为1 且状态为0表示用户已被禁用
|
||||||
|
if user.ID != 1 && user.Status == 0 {
|
||||||
|
return nil, errs.ErrUserDisabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = comparePasswordHash(user.PasswordHash, req.Password); err != nil {
|
||||||
|
return nil, errs.ErrInvalidCredentials
|
||||||
|
}
|
||||||
|
|
||||||
|
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshToken, err := s.jwt.GenerateRefreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 哈希
|
||||||
|
hash := s.jwt.HashRefreshToken(refreshToken)
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
refreshTokenExp := now.Add(s.cfg.JWTConfig.RefreshExpire)
|
||||||
|
refreshTokenRecord := &auth.RefreshTokenRecord{
|
||||||
|
UserID: user.ID,
|
||||||
|
CreatedAt: now,
|
||||||
|
ExpiresAt: refreshTokenExp,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存入redis
|
||||||
|
err = s.cache.SetJSON(ctx, cachekey.AuthRefresh(hash), refreshTokenRecord, s.cfg.JWTConfig.RefreshExpire)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反向索引
|
||||||
|
err = s.cache.SAdd(ctx, cachekey.AuthRefreshUser(user.ID), hash)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给反向索引设置过期时间 这个过期时间需要覆盖最后一个token的过期时间 所以用最新的就行了
|
||||||
|
if err = s.cache.Expire(ctx, cachekey.AuthRefreshUser(user.ID), s.cfg.JWTConfig.RefreshExpire); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.LoginResponse{
|
||||||
|
AccessToken: accessToken,
|
||||||
|
AccessTokenExp: accessTokenExp,
|
||||||
|
RefreshToken: refreshToken,
|
||||||
|
RefreshTokenExp: refreshTokenExp,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) Logout(ctx context.Context, refreshToken string) error {
|
||||||
|
hash := s.jwt.HashRefreshToken(refreshToken)
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
user, ok, err := cache.GetJSON[auth.RefreshTokenRecord](ctx, s.cache, cachekey.AuthRefresh(hash))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
// 清理反向索引
|
||||||
|
if err = s.cache.SRem(ctx, cachekey.AuthRefreshUser(user.UserID), hash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理当前登录的token
|
||||||
|
if err = s.cache.Del(ctx, cachekey.AuthRefresh(hash)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) GetActiveSysUser(ctx context.Context, id int32) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
_, err = s.store.GetActiveSysUserByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AuthService) RefreshToken(ctx context.Context, refreshToken string) (*response.LoginResponse, error) {
|
||||||
|
// 获取哈希
|
||||||
|
hash := s.jwt.HashRefreshToken(refreshToken)
|
||||||
|
// 从redis中获取数据
|
||||||
|
record, ok, err := cache.GetJSON[auth.RefreshTokenRecord](ctx, s.cache, cachekey.AuthRefresh(hash))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrInvalidRefreshToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, errs.ErrInvalidRefreshToken
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是超级用户 需要判断用户状态
|
||||||
|
if record.UserID != 1 {
|
||||||
|
err = s.GetActiveSysUser(ctx, record.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.ErrInvalidRefreshToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取新的access token
|
||||||
|
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(record.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.LoginResponse{
|
||||||
|
AccessToken: accessToken,
|
||||||
|
AccessTokenExp: accessTokenExp,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/common"
|
"server/internal/model/common"
|
||||||
"server/internal/model/request"
|
"server/internal/model/request"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
@@ -10,27 +11,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CategoryService struct {
|
type CategoryService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCategoryService(queries *db.Queries) *CategoryService {
|
func NewCategoryService(store *db.Store) *CategoryService {
|
||||||
return &CategoryService{
|
return &CategoryService{
|
||||||
queries: queries,
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([]db.Category, int64, error) {
|
func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.Category, int64, error) {
|
||||||
params := db.ListCategoriesParams{
|
params := sqlc.ListCategoriesParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountCategories(ctx)
|
total, err := s.store.CountCategories(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.ListCategories(ctx, params)
|
list, err := s.store.ListCategories(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -38,27 +39,29 @@ func (s *CategoryService) ListPage(ctx context.Context, p *common.Pagination) ([
|
|||||||
return list, total, nil
|
return list, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CategoryService) ListAll(ctx context.Context) ([]db.Category, error) {
|
func (s *CategoryService) ListAll(ctx context.Context) ([]sqlc.Category, error) {
|
||||||
return s.queries.ListAllCategories(ctx)
|
return s.store.ListAllCategories(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CategoryService) Create(ctx context.Context, req request.CreateCategoryRequest) error {
|
func (s *CategoryService) Create(ctx context.Context, req request.CreateCategoryRequest) error {
|
||||||
params := db.CreateCategoryParams{
|
params := sqlc.CreateCategoryParams{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Code: req.Code,
|
Code: req.Code,
|
||||||
|
Sort: req.Sort,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.queries.CreateCategory(ctx, params)
|
err := s.store.CreateCategory(ctx, params)
|
||||||
return dberr.MapUniqueViolation(err, dberr.CategoryCodeKey, errs.ErrCategoryCodeAlreadyExists)
|
return dberr.MapUniqueViolation(err, dberr.CategoryCodeKey, errs.ErrCategoryCodeAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CategoryService) Update(ctx context.Context, id int32, req request.UpdateCategoryRequest) error {
|
func (s *CategoryService) Update(ctx context.Context, id int32, req request.UpdateCategoryRequest) error {
|
||||||
params := db.UpdateCategoryParams{
|
params := sqlc.UpdateCategoryParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Code: req.Code,
|
Code: req.Code,
|
||||||
|
Sort: req.Sort,
|
||||||
}
|
}
|
||||||
rows, err := s.queries.UpdateCategory(ctx, params)
|
rows, err := s.store.UpdateCategory(ctx, params)
|
||||||
// 先判断数据条目是否存在
|
// 先判断数据条目是否存在
|
||||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound); err != nil {
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound); err != nil {
|
||||||
// 再判断code是否重复
|
// 再判断code是否重复
|
||||||
@@ -69,6 +72,13 @@ func (s *CategoryService) Update(ctx context.Context, id int32, req request.Upda
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *CategoryService) Delete(ctx context.Context, id int32) error {
|
func (s *CategoryService) Delete(ctx context.Context, id int32) error {
|
||||||
rows, err := s.queries.DeleteCategory(ctx, id)
|
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
err := q.DeletePostCategoryByCategoryID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := q.DeleteCategory(ctx, id)
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound)
|
return dberr.MapRowsAffected(rows, err, errs.ErrCategoryNotFound)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ var Module = fx.Module("services",
|
|||||||
NewSysApiService,
|
NewSysApiService,
|
||||||
NewSysFileService,
|
NewSysFileService,
|
||||||
NewSysPostService,
|
NewSysPostService,
|
||||||
|
NewAuthService,
|
||||||
|
NewTagService,
|
||||||
|
|
||||||
NewPostService,
|
NewPostService,
|
||||||
NewCategoryService,
|
NewCategoryService,
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/common"
|
"server/internal/model/common"
|
||||||
"server/internal/model/response"
|
"server/internal/model/response"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
@@ -12,27 +13,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PostService struct {
|
type PostService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostService(queries *db.Queries) *PostService {
|
func NewPostService(store *db.Store) *PostService {
|
||||||
return &PostService{
|
return &PostService{
|
||||||
queries: queries,
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListPublishedPostsRow, int64, error) {
|
func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListPublishedPostsRow, int64, error) {
|
||||||
params := db.ListPublishedPostsParams{
|
params := sqlc.ListPublishedPostsParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountPublishedPosts(ctx)
|
total, err := s.store.CountPublishedPosts(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.ListPublishedPosts(ctx, params)
|
list, err := s.store.ListPublishedPosts(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -45,13 +46,13 @@ func (s *PostService) ListPage(ctx context.Context, p *common.Pagination) ([]db.
|
|||||||
return list, total, nil
|
return list, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (*db.GetPublicPostBySlugRow, error) {
|
func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (*sqlc.GetPublicPostBySlugRow, error) {
|
||||||
post, err := s.queries.GetPublicPostBySlug(ctx, slug)
|
post, err := s.store.GetPublicPostBySlug(ctx, slug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, dberr.MapNoRows(err, errs.ErrPostNotFound)
|
return nil, dberr.MapNoRows(err, errs.ErrPostNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = s.queries.IncrementPostStatsView(ctx, db.IncrementPostStatsViewParams{
|
_ = s.store.IncrementPostStatsView(ctx, sqlc.IncrementPostStatsViewParams{
|
||||||
PostID: post.ID,
|
PostID: post.ID,
|
||||||
Ip: ip,
|
Ip: ip,
|
||||||
})
|
})
|
||||||
@@ -59,12 +60,12 @@ func (s *PostService) GetPost(ctx context.Context, slug string, ip netip.Addr) (
|
|||||||
return &post, nil
|
return &post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PostService) ListCategoryStats(ctx context.Context) ([]db.ListCategoryStatsRow, error) {
|
func (s *PostService) ListCategoryStats(ctx context.Context) ([]sqlc.ListCategoryStatsRow, error) {
|
||||||
return s.queries.ListCategoryStats(ctx)
|
return s.store.ListCategoryStats(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PostService) ListArchives(ctx context.Context) ([]response.ArchiveYear, error) {
|
func (s *PostService) ListArchives(ctx context.Context) ([]response.ArchiveYear, error) {
|
||||||
list, err := s.queries.ListArchives(ctx)
|
list, err := s.store.ListArchives(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -111,3 +112,7 @@ func (s *PostService) ListArchives(ctx context.Context) ([]response.ArchiveYear,
|
|||||||
|
|
||||||
return archive, nil
|
return archive, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PostService) ListPostTags(ctx context.Context) ([]sqlc.Tag, error) {
|
||||||
|
return s.store.ListAllTags(ctx)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,41 +2,46 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
"server/internal/model/common"
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/enum"
|
"server/internal/model/enum"
|
||||||
"server/internal/model/request"
|
"server/internal/model/request"
|
||||||
"server/internal/pkg/cache"
|
"server/internal/pkg/cache"
|
||||||
|
"server/internal/pkg/cache/cachekey"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysApiService struct {
|
type SysApiService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
pool *pgxpool.Pool
|
|
||||||
cache *cache.Caches
|
cache *cache.Caches
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysApiService(queries *db.Queries, pool *pgxpool.Pool, cache *cache.Caches) *SysApiService {
|
func NewSysApiService(store *db.Store, cache *cache.Caches) *SysApiService {
|
||||||
return &SysApiService{queries: queries, pool: pool, cache: cache}
|
return &SysApiService{
|
||||||
|
store: store,
|
||||||
|
cache: cache,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysApiService) ListPage(ctx context.Context, p *common.Pagination) ([]db.SysApi, int64, error) {
|
func (s *SysApiService) ListPage(ctx context.Context, p request.SearchSysApiParams) ([]sqlc.SysApi, int64, error) {
|
||||||
params := db.GetSysApisParams{
|
params := sqlc.GetSysApisParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
|
GroupName: p.GroupName,
|
||||||
|
Method: p.Method,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountSysApis(ctx)
|
total, err := s.store.CountSysApis(ctx, sqlc.CountSysApisParams{
|
||||||
|
GroupName: p.GroupName,
|
||||||
|
Method: p.Method,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.GetSysApis(ctx, params)
|
list, err := s.store.GetSysApis(ctx, params)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -45,27 +50,17 @@ func (s *SysApiService) ListPage(ctx context.Context, p *common.Pagination) ([]d
|
|||||||
return list, total, nil
|
return list, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysApiService) GetAllSysApis(ctx context.Context) ([]db.SysApi, error) {
|
func (s *SysApiService) GetAllSysApis(ctx context.Context) ([]sqlc.SysApi, error) {
|
||||||
return s.queries.GetAllSysApis(ctx)
|
return s.store.GetAllSysApis(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysApiService) GetApiGroupNames(ctx context.Context) ([]string, error) {
|
func (s *SysApiService) GetApiGroupNames(ctx context.Context) ([]string, error) {
|
||||||
return s.queries.GetSysApiGroupNames(ctx)
|
return s.store.GetSysApiGroupNames(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequest) error {
|
func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequest) error {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
if err != nil {
|
api := sqlc.CreateSysApiParams{
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func(ctx context.Context) {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}(ctx)
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
api := db.CreateSysApiParams{
|
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
GroupName: req.GroupName,
|
GroupName: req.GroupName,
|
||||||
Method: req.Method,
|
Method: req.Method,
|
||||||
@@ -74,7 +69,7 @@ func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建权限
|
// 创建权限
|
||||||
permissionId, err := q.CreateSysPermission(ctx, db.CreateSysPermissionParams{
|
permissionId, err := q.CreateSysPermission(ctx, sqlc.CreateSysPermissionParams{
|
||||||
Type: int16(enum.PermissionTypeApi),
|
Type: int16(enum.PermissionTypeApi),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -89,25 +84,20 @@ func (s *SysApiService) Create(ctx context.Context, req request.CreateSysApiRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关联权限
|
// 关联权限
|
||||||
if err = q.CreateSysApiPermission(ctx, db.CreateSysApiPermissionParams{
|
if err = q.CreateSysApiPermission(ctx, sqlc.CreateSysApiPermissionParams{
|
||||||
ApiID: apiId,
|
ApiID: apiId,
|
||||||
PermissionID: permissionId,
|
PermissionID: permissionId,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = tx.Commit(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理缓存
|
|
||||||
s.cache.ClearAllSysUserCache()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysApiService) Update(ctx context.Context, id int32, req request.UpdateSysApiRequest) error {
|
func (s *SysApiService) Update(ctx context.Context, id int32, req request.UpdateSysApiRequest) error {
|
||||||
api := db.UpdateSysApiParams{
|
api := sqlc.UpdateSysApiParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
GroupName: req.GroupName,
|
GroupName: req.GroupName,
|
||||||
@@ -116,43 +106,51 @@ func (s *SysApiService) Update(ctx context.Context, id int32, req request.Update
|
|||||||
Sort: req.Sort,
|
Sort: req.Sort,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
rows, err := s.store.UpdateSysApi(ctx, api)
|
||||||
s.cache.ClearAllSysUserCache()
|
|
||||||
|
|
||||||
rows, err := s.queries.UpdateSysApi(ctx, api)
|
|
||||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil {
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil {
|
||||||
return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrSysApiMethodPathAlreadyExists)
|
return dberr.MapUniqueViolation(err, dberr.SysApisMethodPathKey, errs.ErrSysApiMethodPathAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果更新成功 则清理缓存
|
||||||
|
_ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysApiService) Delete(ctx context.Context, id int32) error {
|
func (s *SysApiService) Delete(ctx context.Context, id int32) error {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
// 1. 找出权限id
|
||||||
|
permissionID, err := q.GetSysPermissionIDBySysApiID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return dberr.MapNoRows(err, errs.ErrSysApiNotFound)
|
||||||
|
}
|
||||||
|
// 2. 根据权限id,删除角色权限关联数据
|
||||||
|
if err = q.DeleteSysRolePermissionByPermissionID(ctx, permissionID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// 3. 根据api id 删除sys_api_permission关联表数据
|
||||||
defer tx.Rollback(ctx)
|
if err = q.DeleteSysApiPermission(ctx, id); err != nil {
|
||||||
|
return err
|
||||||
q := db.New(tx)
|
}
|
||||||
|
// 4. 根据权限id,删除权限表权限数据
|
||||||
|
if err = q.DeleteSysPermission(ctx, permissionID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 5. 删除api
|
||||||
rows, err := q.DeleteSysApi(ctx, id)
|
rows, err := q.DeleteSysApi(ctx, id)
|
||||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil {
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysApiNotFound); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.DeleteSysPermissionBySysApiID(ctx, id); err != nil {
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.DeleteSysApiPermission(ctx, id); err != nil {
|
// 清理缓存 事务执行成功后才清理
|
||||||
return err
|
_ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern)
|
||||||
}
|
|
||||||
|
|
||||||
// 清理缓存
|
return nil
|
||||||
s.cache.ClearAllSysUserCache()
|
|
||||||
|
|
||||||
return tx.Commit(ctx)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import (
|
|||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/common"
|
"server/internal/model/common"
|
||||||
"server/internal/model/response"
|
"server/internal/model/response"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
@@ -14,12 +15,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SysFileService struct {
|
type SysFileService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysFileService(queries *db.Queries) *SysFileService {
|
func NewSysFileService(store *db.Store) *SysFileService {
|
||||||
return &SysFileService{
|
return &SysFileService{
|
||||||
queries: queries,
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,19 +41,19 @@ func MakeSavedDir(folder string) (string, error) {
|
|||||||
return uploadDir, nil
|
return uploadDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]db.File, int64, error) {
|
func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.File, int64, error) {
|
||||||
params := db.GetFilesParams{
|
params := sqlc.GetFilesParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountFiles(ctx)
|
total, err := s.store.CountFiles(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.GetFiles(ctx, params)
|
list, err := s.store.GetFiles(ctx, params)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -61,7 +62,7 @@ func (s *SysFileService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
|||||||
return response.ToFiles(list), total, nil
|
return response.ToFiles(list), total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysFileService) Upload(ctx context.Context, folder string, file *multipart.FileHeader) (*db.CreateFileRow, error) {
|
func (s *SysFileService) Upload(ctx context.Context, folder string, file *multipart.FileHeader) (*sqlc.CreateFileRow, error) {
|
||||||
// 生成文件名
|
// 生成文件名
|
||||||
fileID, err := gonanoid.New()
|
fileID, err := gonanoid.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -98,7 +99,7 @@ func (s *SysFileService) Upload(ctx context.Context, folder string, file *multip
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
params := db.CreateFileParams{
|
params := sqlc.CreateFileParams{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
FilePath: filePath,
|
FilePath: filePath,
|
||||||
OriginalName: file.Filename,
|
OriginalName: file.Filename,
|
||||||
@@ -107,7 +108,7 @@ func (s *SysFileService) Upload(ctx context.Context, folder string, file *multip
|
|||||||
FileSize: file.Size,
|
FileSize: file.Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := s.queries.CreateFile(ctx, params)
|
result, err := s.store.CreateFile(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,43 +2,32 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/common"
|
"server/internal/model/common"
|
||||||
"server/internal/model/enum"
|
"server/internal/model/enum"
|
||||||
"server/internal/model/request"
|
"server/internal/model/request"
|
||||||
|
"server/internal/pkg/cache"
|
||||||
|
"server/internal/pkg/cache/cachekey"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysMenuService struct {
|
type SysMenuService struct {
|
||||||
pool *pgxpool.Pool
|
store *db.Store
|
||||||
queries *db.Queries
|
cache *cache.Caches
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysMenuService(queries *db.Queries, pool *pgxpool.Pool) *SysMenuService {
|
func NewSysMenuService(store *db.Store, cache *cache.Caches) *SysMenuService {
|
||||||
return &SysMenuService{
|
return &SysMenuService{
|
||||||
queries: queries,
|
store: store,
|
||||||
pool: pool,
|
cache: cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRequest) error {
|
func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRequest) error {
|
||||||
// 开启事务
|
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
menu := sqlc.CreateSysMenuParams{
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func(ctx context.Context) {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}(ctx)
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
menu := db.CreateSysMenuParams{
|
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Path: req.Path,
|
Path: req.Path,
|
||||||
Component: req.Component,
|
Component: req.Component,
|
||||||
@@ -47,30 +36,38 @@ func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRe
|
|||||||
Hidden: req.Hidden,
|
Hidden: req.Hidden,
|
||||||
Sort: req.Sort,
|
Sort: req.Sort,
|
||||||
Icon: req.Icon,
|
Icon: req.Icon,
|
||||||
|
ParentID: req.ParentID,
|
||||||
}
|
}
|
||||||
|
|
||||||
permissionId, err := q.CreateSysPermission(ctx, db.CreateSysPermissionParams{
|
// 先创建权限
|
||||||
|
permissionId, err := q.CreateSysPermission(ctx, sqlc.CreateSysPermissionParams{
|
||||||
Type: int16(enum.PermissionTypeMenu),
|
Type: int16(enum.PermissionTypeMenu),
|
||||||
Code: &req.PermissionCode,
|
Code: &req.PermissionCode,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// code重复性校验
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建菜单
|
||||||
menuId, err := q.CreateSysMenu(ctx, menu)
|
menuId, err := q.CreateSysMenu(ctx, menu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrSysMenusPathUniqueIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.CreateSysMenuPermission(ctx, db.CreateSysMenuPermissionParams{
|
// 绑定权限
|
||||||
|
if err = q.CreateSysMenuPermission(ctx, sqlc.CreateSysMenuPermissionParams{
|
||||||
MenuID: menuId,
|
MenuID: menuId,
|
||||||
PermissionID: permissionId,
|
PermissionID: permissionId,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = tx.Commit(ctx); err != nil {
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,16 +75,9 @@ func (s *SysMenuService) Create(ctx context.Context, req request.CreateSysMenuRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysMenuService) Update(ctx context.Context, id int32, req request.UpdateSysMenuRequest) error {
|
func (s *SysMenuService) Update(ctx context.Context, id int32, req request.UpdateSysMenuRequest) error {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tx.Rollback(ctx)
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
// 构造 menu 参数
|
// 构造 menu 参数
|
||||||
menu := db.UpdateSysMenuParams{
|
menu := sqlc.UpdateSysMenuParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Path: req.Path,
|
Path: req.Path,
|
||||||
@@ -96,7 +86,13 @@ func (s *SysMenuService) Update(ctx context.Context, id int32, req request.Updat
|
|||||||
Sort: req.Sort,
|
Sort: req.Sort,
|
||||||
Type: req.Type,
|
Type: req.Type,
|
||||||
Status: req.Status,
|
Status: req.Status,
|
||||||
Icon: req.Icon,
|
}
|
||||||
|
|
||||||
|
if req.Icon.Set {
|
||||||
|
menu.UpdateIcon = true
|
||||||
|
if req.Icon.Valid {
|
||||||
|
menu.Icon = &req.Icon.Value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.ParentID.Set {
|
if req.ParentID.Set {
|
||||||
@@ -109,11 +105,12 @@ func (s *SysMenuService) Update(ctx context.Context, id int32, req request.Updat
|
|||||||
// 执行更新
|
// 执行更新
|
||||||
rows, err := q.UpdateSysMenu(ctx, menu)
|
rows, err := q.UpdateSysMenu(ctx, menu)
|
||||||
|
|
||||||
|
// 先判断数据是否存在 在判断path是否重复
|
||||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
||||||
return err
|
return dberr.MapUniqueViolation(err, dberr.SysMenusPathUniqueIdx, errs.ErrSysMenusPathUniqueIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
permission := db.UpdateSysMenuPermissionCodeParams{
|
permission := sqlc.UpdateSysMenuPermissionCodeParams{
|
||||||
MenuID: id,
|
MenuID: id,
|
||||||
Code: req.PermissionCode,
|
Code: req.PermissionCode,
|
||||||
}
|
}
|
||||||
@@ -122,22 +119,32 @@ func (s *SysMenuService) Update(ctx context.Context, id int32, req request.Updat
|
|||||||
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
return dberr.MapUniqueViolation(err, dberr.SysPermissionsCodeKey, errs.ErrPermissionCodeAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.Commit(ctx)
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理缓存
|
||||||
|
_ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListSysMenusRow, int64, error) {
|
func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListSysMenusRow, int64, error) {
|
||||||
params := db.ListSysMenusParams{
|
params := sqlc.ListSysMenusParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountSysMenus(ctx)
|
total, err := s.store.CountSysMenus(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.ListSysMenus(ctx, params)
|
list, err := s.store.ListSysMenus(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -145,34 +152,43 @@ func (s *SysMenuService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
|||||||
return list, total, nil
|
return list, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysMenuService) GetMenus(ctx context.Context) ([]db.GetAllSysMenusRow, error) {
|
func (s *SysMenuService) GetMenus(ctx context.Context) ([]sqlc.GetAllSysMenusRow, error) {
|
||||||
return s.queries.GetAllSysMenus(ctx)
|
return s.store.GetAllSysMenus(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysMenuService) Delete(ctx context.Context, id int32) error {
|
func (s *SysMenuService) Delete(ctx context.Context, id int32) error {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
// 1. 根据菜单id, 找出对应的权限id
|
||||||
|
permissionID, err := q.GetSysPermissionIDBySysMenuID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return dberr.MapNoRows(err, errs.ErrSysMenuNotFound)
|
||||||
|
}
|
||||||
|
// 2. 根据权限id 删除角色权限关联
|
||||||
|
if err = q.DeleteSysRolePermissionByPermissionID(ctx, permissionID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 3. 根据菜单id 删除sys_menu_permission关联表数据
|
||||||
|
if err = q.DeleteSysMenuPermission(ctx, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 4. 根据权限id删除权限表数据
|
||||||
|
if err = q.DeleteSysPermission(ctx, permissionID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 5. 删除菜单
|
||||||
|
rows, err := q.DeleteSysMenu(ctx, id)
|
||||||
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer tx.Rollback(ctx)
|
// 清理缓存
|
||||||
|
_ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern)
|
||||||
q := db.New(tx)
|
return nil
|
||||||
|
|
||||||
rows, err := q.DeleteSysMenu(ctx, id)
|
|
||||||
|
|
||||||
if err = dberr.MapRowsAffected(rows, err, errs.ErrSysMenuNotFound); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = q.DeleteSysPermissionByMenuID(ctx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = q.DeleteSysMenuPermission(ctx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tx.Commit(ctx)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,41 +2,37 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
db "server/internal/db/sqlc"
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/common"
|
"server/internal/model/common"
|
||||||
"server/internal/model/request"
|
"server/internal/model/request"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysPostService struct {
|
type SysPostService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
pool *pgxpool.Pool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysPostService(queries *db.Queries, pool *pgxpool.Pool) *SysPostService {
|
func NewSysPostService(store *db.Store) *SysPostService {
|
||||||
return &SysPostService{
|
return &SysPostService{
|
||||||
queries: queries,
|
store: store,
|
||||||
pool: pool,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListPostsRow, int64, error) {
|
func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.ListPostsRow, int64, error) {
|
||||||
params := db.ListPostsParams{
|
params := sqlc.ListPostsParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountPosts(ctx)
|
total, err := s.store.CountPosts(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.ListPosts(ctx, params)
|
list, err := s.store.ListPosts(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -49,8 +45,8 @@ func (s *SysPostService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
|||||||
return list, total, nil
|
return list, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysPostService) FindByID(ctx context.Context, id int32) (*db.GetPostByIdRow, error) {
|
func (s *SysPostService) FindByID(ctx context.Context, id int32) (*sqlc.GetPostByIdRow, error) {
|
||||||
post, err := s.queries.GetPostById(ctx, id)
|
post, err := s.store.GetPostById(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, dberr.MapNoRows(err, errs.ErrPostNotFound)
|
return nil, dberr.MapNoRows(err, errs.ErrPostNotFound)
|
||||||
}
|
}
|
||||||
@@ -61,21 +57,10 @@ func (s *SysPostService) FindByID(ctx context.Context, id int32) (*db.GetPostByI
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysPostService) Create(ctx context.Context, req request.CreatePostRequest) (int32, error) {
|
func (s *SysPostService) Create(ctx context.Context, req request.CreatePostRequest) (int32, error) {
|
||||||
// 开启事务
|
postID, err := db.WithTxResult(ctx, s.store, func(q *sqlc.Queries) (int32, error) {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
params := sqlc.CreatePostParams{
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
params := db.CreatePostParams{
|
|
||||||
Title: req.Title,
|
Title: req.Title,
|
||||||
CoverID: req.CoverID,
|
CoverID: &req.CoverID,
|
||||||
Slug: req.Slug,
|
Slug: req.Slug,
|
||||||
Content: req.Content,
|
Content: req.Content,
|
||||||
Summary: req.Summary,
|
Summary: req.Summary,
|
||||||
@@ -84,40 +69,44 @@ func (s *SysPostService) Create(ctx context.Context, req request.CreatePostReque
|
|||||||
PublishedAt: req.PublishedAt,
|
PublishedAt: req.PublishedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
postId, err := q.CreatePost(ctx, params)
|
// 创建博客
|
||||||
|
postID, err := q.CreatePost(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, dberr.MapUniqueViolation(err, dberr.PostSlugKey, errs.ErrSlugAlreadyExists)
|
return 0, dberr.MapUniqueViolation(err, dberr.PostSlugKey, errs.ErrSlugAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.CreatePostCategory(ctx, db.CreatePostCategoryParams{
|
// 关联分类
|
||||||
PostID: postId,
|
if err = q.CreatePostCategory(ctx, sqlc.CreatePostCategoryParams{
|
||||||
|
PostID: postID,
|
||||||
CategoryID: *req.CategoryID,
|
CategoryID: *req.CategoryID,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交
|
var tagsParams []sqlc.CreatePostTagParams
|
||||||
if err = tx.Commit(ctx); err != nil {
|
for _, tagID := range req.Tags {
|
||||||
|
tagsParams = append(tagsParams, sqlc.CreatePostTagParams{
|
||||||
|
PostID: postID,
|
||||||
|
TagID: tagID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = q.CreatePostTag(ctx, tagsParams); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return postId, nil
|
return postID, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return postID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysPostService) Update(ctx context.Context, id int32, req request.UpdatePostRequest) error {
|
func (s *SysPostService) Update(ctx context.Context, id int32, req request.UpdatePostRequest) error {
|
||||||
// 开启事务
|
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
params := sqlc.UpdatePostParams{
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
params := db.UpdatePostParams{
|
|
||||||
Title: req.Title,
|
Title: req.Title,
|
||||||
CoverID: req.CoverID,
|
CoverID: req.CoverID,
|
||||||
Slug: req.Slug,
|
Slug: req.Slug,
|
||||||
@@ -138,26 +127,47 @@ func (s *SysPostService) Update(ctx context.Context, id int32, req request.Updat
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.DeletePostCategory(ctx, id); err != nil {
|
// 先删除分类
|
||||||
|
if err = q.DeletePostCategoryByPostID(ctx, id); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.CreatePostCategory(ctx, db.CreatePostCategoryParams{
|
if err = q.CreatePostCategory(ctx, sqlc.CreatePostCategoryParams{
|
||||||
PostID: id,
|
PostID: id,
|
||||||
CategoryID: *req.CategoryID,
|
CategoryID: *req.CategoryID,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交
|
// 先删除标签
|
||||||
if err = tx.Commit(ctx); err != nil {
|
if err = q.DeletePostTagByPostID(ctx, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagsParams []sqlc.CreatePostTagParams
|
||||||
|
for _, tagID := range req.Tags {
|
||||||
|
tagsParams = append(tagsParams, sqlc.CreatePostTagParams{
|
||||||
|
PostID: id,
|
||||||
|
TagID: tagID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = q.CreatePostTag(ctx, tagsParams); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysPostService) Delete(ctx context.Context, id int32) error {
|
func (s *SysPostService) Delete(ctx context.Context, id int32) error {
|
||||||
rows, err := s.queries.DeletePost(ctx, id)
|
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
err := q.DeletePostCategoryByPostID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := q.DeletePost(ctx, id)
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrPostNotFound)
|
return dberr.MapRowsAffected(rows, err, errs.ErrPostNotFound)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,45 +2,56 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
db "server/internal/db/sqlc"
|
"errors"
|
||||||
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/model/common"
|
"server/internal/model/common"
|
||||||
"server/internal/model/enum"
|
"server/internal/model/enum"
|
||||||
"server/internal/model/request"
|
"server/internal/model/request"
|
||||||
"server/internal/pkg/cache"
|
"server/internal/pkg/cache"
|
||||||
|
"server/internal/pkg/cache/cachekey"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysRoleService struct {
|
type SysRoleService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
pool *pgxpool.Pool
|
|
||||||
cache *cache.Caches
|
cache *cache.Caches
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysRoleService(queries *db.Queries, pool *pgxpool.Pool, cache *cache.Caches) *SysRoleService {
|
func NewSysRoleService(store *db.Store, cache *cache.Caches) *SysRoleService {
|
||||||
return &SysRoleService{
|
return &SysRoleService{
|
||||||
queries: queries,
|
store: store,
|
||||||
pool: pool,
|
|
||||||
cache: cache,
|
cache: cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) ListPage(ctx context.Context, p *common.Pagination) ([]db.SysRole, int64, error) {
|
// clearCache 清理所有api权限和用户信息 禁用角色/删除角色需要清理
|
||||||
params := db.ListSysRolesParams{
|
func (s *SysRoleService) clearCache(ctx context.Context) error {
|
||||||
|
if err := s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SysRoleService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.SysRole, int64, error) {
|
||||||
|
params := sqlc.ListSysRolesParams{
|
||||||
Limit: p.PageSize,
|
Limit: p.PageSize,
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountSysRoles(ctx)
|
total, err := s.store.CountSysRoles(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := s.queries.ListSysRoles(ctx, params)
|
list, err := s.store.ListSysRoles(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -48,38 +59,36 @@ func (s *SysRoleService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
|||||||
return list, total, nil
|
return list, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) GetRoles(ctx context.Context) ([]db.SysRole, error) {
|
func (s *SysRoleService) GetRoles(ctx context.Context) ([]sqlc.SysRole, error) {
|
||||||
return s.queries.GetAllSysRoles(ctx)
|
return s.store.GetAllSysRoles(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) GetRoleMenus(ctx context.Context, id int32) ([]db.GetSysRoleMenusRow, error) {
|
func (s *SysRoleService) GetRoleMenus(ctx context.Context, id int32) ([]sqlc.GetSysRoleMenusRow, error) {
|
||||||
_, err := s.queries.GetSysRoleByID(ctx, id)
|
_, err := s.store.GetSysRoleByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
return nil, dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.queries.GetSysRoleMenus(ctx, id)
|
return s.store.GetSysRoleMenus(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) GetRoleApis(ctx context.Context, id int32) ([]db.GetSysRoleApisRow, error) {
|
func (s *SysRoleService) GetRoleApis(ctx context.Context, id int32) ([]sqlc.GetSysRoleApisRow, error) {
|
||||||
_, err := s.queries.GetSysRoleByID(ctx, id)
|
_, err := s.store.GetSysRoleByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
return nil, dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.queries.GetSysRoleApis(ctx, id)
|
return s.store.GetSysRoleApis(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) Create(ctx context.Context, req request.CreateSysRoleRequest) error {
|
func (s *SysRoleService) Create(ctx context.Context, req request.CreateSysRoleRequest) error {
|
||||||
params := db.CreateSysRoleParams{
|
params := sqlc.CreateSysRoleParams{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Code: req.Code,
|
Code: req.Code,
|
||||||
|
Status: *req.Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
err := s.store.CreateSysRole(ctx, params)
|
||||||
s.cache.ClearAllSysUserCache()
|
|
||||||
|
|
||||||
err := s.queries.CreateSysRole(ctx, params)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dberr.MapUniqueViolation(err, dberr.SysRoleCodeKey, errs.ErrCodeAlreadyExists)
|
return dberr.MapUniqueViolation(err, dberr.SysRoleCodeKey, errs.ErrCodeAlreadyExists)
|
||||||
}
|
}
|
||||||
@@ -87,125 +96,156 @@ func (s *SysRoleService) Create(ctx context.Context, req request.CreateSysRoleRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) Update(ctx context.Context, id int32, req request.UpdateSysRoleRequest) error {
|
func (s *SysRoleService) Update(ctx context.Context, id int32, req request.UpdateSysRoleRequest) error {
|
||||||
params := db.UpdateSysRoleParams{
|
params := sqlc.UpdateSysRoleParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
|
Status: req.Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
rows, err := s.store.UpdateSysRole(ctx, params)
|
||||||
s.cache.ClearAllSysUserCache()
|
err = dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errs.ErrSysRoleNotFound) {
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
rows, err := s.queries.UpdateSysRole(ctx, params)
|
// 清理缓存 无论启用禁用 启用时也需要清理缓存以刷新权限
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound)
|
_ = s.clearCache(ctx)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysRoleService) SetRoleMenus(ctx context.Context, roleID int32, req request.SetSysRoleMenusRequest) error {
|
func (s *SysRoleService) SetRoleMenus(ctx context.Context, roleID int32, req request.SetSysRoleMenusRequest) error {
|
||||||
_, err := s.queries.GetSysRoleByID(ctx, roleID)
|
// 先检查角色是否存在
|
||||||
|
_, err := s.store.GetSysRoleByID(ctx, roleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
return dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
err = s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
if err != nil {
|
// 1. 先删除所有关联表数据
|
||||||
return err
|
if err = q.DeleteSysRolePermissionByType(ctx, sqlc.DeleteSysRolePermissionByTypeParams{
|
||||||
}
|
|
||||||
|
|
||||||
defer func(ctx context.Context) {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}(ctx)
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
permissionIds, err := s.queries.GetSysPermissionIdsByMenuIDs(ctx, req.MenuIDs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
params := make([]db.CreateSysRolePermissionParams, 0, len(permissionIds))
|
|
||||||
for _, id := range permissionIds {
|
|
||||||
params = append(params, db.CreateSysRolePermissionParams{
|
|
||||||
RoleID: roleID,
|
|
||||||
PermissionID: id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = q.DeleteSysRolePermission(ctx, db.DeleteSysRolePermissionParams{
|
|
||||||
RoleID: roleID,
|
RoleID: roleID,
|
||||||
Type: int16(enum.PermissionTypeMenu),
|
Type: int16(enum.PermissionTypeMenu),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = q.CreateSysRolePermission(ctx, params)
|
// 2. 查出所有权限id并组装数据
|
||||||
|
permissionIds, err := q.GetSysPermissionIdsByMenuIDs(ctx, req.MenuIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = tx.Commit(ctx); err != nil {
|
params := make([]sqlc.CreateSysRolePermissionParams, 0, len(permissionIds))
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理缓存
|
|
||||||
s.cache.ClearAllSysUserCache()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SysRoleService) SetRoleApis(ctx context.Context, roleID int32, req request.SetSysRoleApisRequest) error {
|
|
||||||
_, err := s.queries.GetSysRoleByID(ctx, roleID)
|
|
||||||
if err != nil {
|
|
||||||
return dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func(ctx context.Context) {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}(ctx)
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
permissionIds, err := s.queries.GetSysPermissionIdsByApiIDs(ctx, req.ApiIDs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
params := make([]db.CreateSysRolePermissionParams, 0, len(permissionIds))
|
|
||||||
for _, id := range permissionIds {
|
for _, id := range permissionIds {
|
||||||
params = append(params, db.CreateSysRolePermissionParams{
|
params = append(params, sqlc.CreateSysRolePermissionParams{
|
||||||
RoleID: roleID,
|
RoleID: roleID,
|
||||||
PermissionID: id,
|
PermissionID: id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = q.DeleteSysRolePermission(ctx, db.DeleteSysRolePermissionParams{
|
// 批量插入
|
||||||
|
_, err = q.CreateSysRolePermission(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理缓存 这里只用清理用户信息
|
||||||
|
_ = s.cache.DelByPrefix(ctx, cachekey.UserInfoPattern)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SysRoleService) SetRoleApis(ctx context.Context, roleID int32, req request.SetSysRoleApisRequest) error {
|
||||||
|
_, err := s.store.GetSysRoleByID(ctx, roleID)
|
||||||
|
if err != nil {
|
||||||
|
return dberr.MapNoRows(err, errs.ErrSysRoleNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
// 先删除所有旧权限数据
|
||||||
|
if err = q.DeleteSysRolePermissionByType(ctx, sqlc.DeleteSysRolePermissionByTypeParams{
|
||||||
RoleID: roleID,
|
RoleID: roleID,
|
||||||
Type: int16(enum.PermissionTypeApi),
|
Type: int16(enum.PermissionTypeApi),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查出数据并组装
|
||||||
|
permissionIds, err := q.GetSysPermissionIdsByApiIDs(ctx, req.ApiIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
params := make([]sqlc.CreateSysRolePermissionParams, 0, len(permissionIds))
|
||||||
|
for _, id := range permissionIds {
|
||||||
|
params = append(params, sqlc.CreateSysRolePermissionParams{
|
||||||
|
RoleID: roleID,
|
||||||
|
PermissionID: id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量插入
|
||||||
_, err = q.CreateSysRolePermission(ctx, params)
|
_, err = q.CreateSysRolePermission(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = tx.Commit(ctx); err != nil {
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理缓存 这里只用清理接口权限
|
||||||
|
_ = s.cache.DelByPrefix(ctx, cachekey.UserApiPermissionsPattern)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SysRoleService) Delete(ctx context.Context, id int32) error {
|
||||||
|
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
// 1.删除角色关联权限
|
||||||
|
err := q.DeleteSysRolePermissionByRoleID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 根据角色id删除用户关联的角色
|
||||||
|
err = q.DeleteSysUserRoleByRoleID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 删除角色信息
|
||||||
|
rows, err := q.DeleteSysRole(ctx, id)
|
||||||
|
err = dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound)
|
||||||
|
if err != nil {
|
||||||
|
// 如果是数据库不存在这条数据 此时也执行清理
|
||||||
|
if errors.Is(err, errs.ErrSysRoleNotFound) {
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
// 清理缓存
|
||||||
s.cache.ClearAllSysUserCache()
|
_ = s.clearCache(ctx)
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SysRoleService) Delete(ctx context.Context, id int32) error {
|
|
||||||
// 清理缓存
|
|
||||||
s.cache.ClearAllSysUserCache()
|
|
||||||
rows, err := s.queries.DeleteSysRole(ctx, id)
|
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrSysRoleNotFound)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,118 +2,91 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
db "server/internal/db/sqlc"
|
"errors"
|
||||||
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
"server/internal/middleware"
|
"server/internal/middleware"
|
||||||
"server/internal/model/common"
|
|
||||||
"server/internal/model/request"
|
"server/internal/model/request"
|
||||||
"server/internal/model/response"
|
"server/internal/model/response"
|
||||||
"server/internal/pkg/cache"
|
"server/internal/pkg/cache"
|
||||||
|
"server/internal/pkg/cache/cachekey"
|
||||||
"server/internal/pkg/dberr"
|
"server/internal/pkg/dberr"
|
||||||
"server/internal/pkg/errs"
|
"server/internal/pkg/errs"
|
||||||
"server/internal/pkg/httputil"
|
"server/internal/pkg/httputil"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysUserService struct {
|
type SysUserService struct {
|
||||||
queries *db.Queries
|
store *db.Store
|
||||||
pool *pgxpool.Pool
|
|
||||||
jwt *middleware.JWTMiddleware
|
jwt *middleware.JWTMiddleware
|
||||||
cache *cache.Caches
|
cache *cache.Caches
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysUserService(queries *db.Queries, pool *pgxpool.Pool, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
|
func NewSysUserService(store *db.Store, jwt *middleware.JWTMiddleware, cache *cache.Caches) *SysUserService {
|
||||||
return &SysUserService{
|
return &SysUserService{
|
||||||
queries: queries,
|
store: store,
|
||||||
pool: pool,
|
|
||||||
jwt: jwt,
|
jwt: jwt,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generatePasswordHash 生成密码哈希
|
// clearUserCache
|
||||||
func generatePasswordHash(password string) (string, error) {
|
func (s *SysUserService) clearUserCache(ctx context.Context, id int32) error {
|
||||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
if err := s.cache.Del(ctx, cachekey.UserApiPermissions(id)); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
return string(hashed), nil
|
|
||||||
|
if err := s.cache.Del(ctx, cachekey.UserInfo(id)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// comparePasswordHash 比较密码哈希
|
// clearRefreshToken
|
||||||
func comparePasswordHash(passwordHash, inputPassword string) error {
|
func (s *SysUserService) clearRefreshToken(ctx context.Context, id int32) error {
|
||||||
return bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(inputPassword))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SysUserService) Login(ctx context.Context, req request.LoginRequest) (*response.LoginResponse, error) {
|
|
||||||
user, err := s.queries.GetSysUserByAccount(ctx, req.Account)
|
|
||||||
|
|
||||||
|
keys, err := s.cache.SMembers(ctx, cachekey.AuthRefreshUser(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.ErrInvalidCredentials
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = comparePasswordHash(user.PasswordHash, req.Password); err != nil {
|
// 循环删除所有的token
|
||||||
return nil, errs.ErrInvalidCredentials
|
for _, key := range keys {
|
||||||
|
if err = s.cache.Del(ctx, cachekey.AuthRefresh(key)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(user.ID)
|
// 删除反向索引
|
||||||
if err != nil {
|
err = s.cache.DelSetMembers(ctx, cachekey.AuthRefreshUser(id))
|
||||||
return nil, err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
refreshToken, refreshTokenExp, err := s.jwt.GenerateRefreshToken(user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &response.LoginResponse{
|
|
||||||
AccessToken: accessToken,
|
|
||||||
AccessTokenExp: accessTokenExp,
|
|
||||||
RefreshToken: refreshToken,
|
|
||||||
RefreshTokenExp: refreshTokenExp,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SysUserService) RefreshToken(ctx context.Context, refreshToken string) (*response.LoginResponse, error) {
|
|
||||||
claims, err := s.jwt.ParseRefreshToken(refreshToken)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errs.ErrInvalidRefreshToken
|
|
||||||
}
|
|
||||||
|
|
||||||
accessToken, accessTokenExp, err := s.jwt.GenerateAccessToken(claims.UserID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &response.LoginResponse{
|
|
||||||
AccessToken: accessToken,
|
|
||||||
AccessTokenExp: accessTokenExp,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool) (*response.SysUserInfo, error) {
|
func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool) (*response.SysUserInfo, error) {
|
||||||
|
return cache.GetOrSetJSON[*response.SysUserInfo](ctx, s.cache, cachekey.UserInfo(id), 0, func() (*response.SysUserInfo, error) {
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
user db.GetSysUserByIDRow
|
user sqlc.GetSysUserByIDRow
|
||||||
roles []db.SysRole
|
roles []sqlc.SysRole
|
||||||
menus []db.SysMenu
|
menus []sqlc.SysMenu
|
||||||
|
permissions []*string
|
||||||
)
|
)
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
u, err := s.queries.GetSysUserByID(ctx, id)
|
u, err := s.store.GetSysUserByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
user = u
|
user = u
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
r, err := s.queries.GetSysUserRoles(ctx, id)
|
r, err := s.store.GetSysUserRoles(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -123,13 +96,34 @@ func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool
|
|||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
var (
|
var (
|
||||||
m []db.SysMenu
|
p []*string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if isAdmin {
|
||||||
|
p, err = s.store.GetSysAdminPermissions(ctx)
|
||||||
|
} else {
|
||||||
|
p, err = s.store.GetSysUserPermissions(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions = p
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
g.Go(func() error {
|
||||||
|
var (
|
||||||
|
m []sqlc.SysMenu
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if isAdmin {
|
if isAdmin {
|
||||||
m, err = s.queries.GetSysAdminMenus(ctx)
|
m, err = s.store.GetSysAdminMenus(ctx)
|
||||||
} else {
|
} else {
|
||||||
m, err = s.queries.GetSysUserMenus(ctx, id)
|
m, err = s.store.GetSysUserMenus(ctx, id)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -142,25 +136,32 @@ func (s *SysUserService) GetUserInfo(ctx context.Context, id int32, isAdmin bool
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理角色
|
// 如果用户被禁用 返回错误 超管不用管状态
|
||||||
userInfo := response.NewSysUserInfo(user, roles, menus)
|
if user.ID != 1 && user.Status != 1 {
|
||||||
|
return nil, errs.ErrUserDisabled
|
||||||
return userInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SysUserService) ListPage(ctx context.Context, p *common.Pagination) ([]db.ListSysUsersRow, int64, error) {
|
|
||||||
params := db.ListSysUsersParams{
|
|
||||||
Limit: p.PageSize,
|
|
||||||
Offset: (p.Page - 1) * p.PageSize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := s.queries.CountSysUsers(ctx)
|
// 处理角色
|
||||||
|
userInfo := response.NewSysUserInfo(user, roles, menus, permissions)
|
||||||
|
|
||||||
|
return userInfo, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SysUserService) ListPage(ctx context.Context, p request.SearchSysUserParams) ([]sqlc.ListSysUsersRow, int64, error) {
|
||||||
|
params := sqlc.ListSysUsersParams{
|
||||||
|
Limit: p.PageSize,
|
||||||
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
|
Username: p.Username,
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := s.store.CountSysUsers(ctx, p.Username)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := s.queries.ListSysUsers(ctx, params)
|
users, err := s.store.ListSysUsers(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -174,14 +175,14 @@ func (s *SysUserService) ListPage(ctx context.Context, p *common.Pagination) ([]
|
|||||||
return users, total, nil
|
return users, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]db.SysRole, error) {
|
func (s *SysUserService) GetRoles(ctx context.Context, id int32) ([]sqlc.SysRole, error) {
|
||||||
// 先查询用户是否存在
|
// 先查询用户是否存在
|
||||||
_, err := s.queries.GetSysUserByID(ctx, id)
|
_, err := s.store.GetSysUserByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
|
return nil, dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.queries.GetSysUserRoles(ctx, id)
|
return s.store.GetSysUserRoles(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRequest) error {
|
func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRequest) error {
|
||||||
@@ -190,14 +191,15 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user := db.CreateSysUserParams{
|
user := sqlc.CreateSysUserParams{
|
||||||
Account: req.Account,
|
Account: req.Account,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
PasswordHash: passwordHash,
|
PasswordHash: passwordHash,
|
||||||
AvatarID: req.AvatarID,
|
AvatarID: req.AvatarID,
|
||||||
|
Status: *req.Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = s.queries.CreateSysUser(ctx, user); err != nil {
|
if err = s.store.CreateSysUser(ctx, user); err != nil {
|
||||||
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
|
return dberr.MapUniqueViolation(err, dberr.SysUserAccountKey, errs.ErrAccountAlreadyExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,9 +207,10 @@ func (s *SysUserService) Create(ctx context.Context, req request.CreateSysUserRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysUserService) Update(ctx context.Context, id int32, req request.UpdateSysUserRequest) error {
|
func (s *SysUserService) Update(ctx context.Context, id int32, req request.UpdateSysUserRequest) error {
|
||||||
user := db.UpdateSysUserParams{
|
user := sqlc.UpdateSysUserParams{
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
ID: id,
|
ID: id,
|
||||||
|
Status: req.Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.AvatarID.Set {
|
if req.AvatarID.Set {
|
||||||
@@ -217,47 +220,59 @@ func (s *SysUserService) Update(ctx context.Context, id int32, req request.Updat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := s.queries.UpdateSysUser(ctx, user)
|
rows, err := s.store.UpdateSysUser(ctx, user)
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error {
|
|
||||||
// 先查询用户是否存在
|
|
||||||
_, err := s.queries.GetSysUserByID(ctx, userID)
|
|
||||||
if err != nil {
|
|
||||||
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func(ctx context.Context) {
|
|
||||||
_ = tx.Rollback(ctx)
|
|
||||||
}(ctx)
|
|
||||||
|
|
||||||
q := db.New(tx)
|
|
||||||
|
|
||||||
if err = q.ClearSysUserRoles(ctx, userID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, roleID := range req.RoleIDs {
|
|
||||||
if err = q.CreateSysUserRole(ctx, db.CreateSysUserRoleParams{
|
|
||||||
UserID: userID,
|
|
||||||
RoleID: roleID,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tx.Commit(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
// 清理缓存
|
||||||
s.cache.ClearSysUserCache(userID)
|
_ = s.clearUserCache(ctx, id)
|
||||||
|
|
||||||
|
// 如果将用户的状态修改为0,则清除用户刷新令牌
|
||||||
|
if req.Status != nil && *req.Status == 0 {
|
||||||
|
_ = s.clearRefreshToken(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SysUserService) SetRoles(ctx context.Context, userID int32, req request.SetSysUserRolesRequest) error {
|
||||||
|
// 先查询用户是否存在
|
||||||
|
_, err := s.store.GetSysUserByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return dberr.MapNoRows(err, errs.ErrUserNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
// 1. 先删除关联数据
|
||||||
|
if err = q.ClearSysUserRoles(ctx, userID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var params []sqlc.CreateSysUserRoleParams
|
||||||
|
|
||||||
|
for _, roleID := range req.RoleIDs {
|
||||||
|
params = append(params, sqlc.CreateSysUserRoleParams{
|
||||||
|
UserID: userID,
|
||||||
|
RoleID: roleID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 关联用户角色数据
|
||||||
|
_, err = q.CreateSysUserRole(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 清理缓存
|
||||||
|
_ = s.clearUserCache(ctx, userID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -268,13 +283,21 @@ func (s *SysUserService) UpdatePassword(ctx context.Context, id int32, req reque
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
params := db.UpdateSysUserPasswordParams{
|
params := sqlc.UpdateSysUserPasswordParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
PasswordHash: passwordHash,
|
PasswordHash: passwordHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := s.queries.UpdateSysUserPassword(ctx, params)
|
rows, err := s.store.UpdateSysUserPassword(ctx, params)
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
err = dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下线当前用户
|
||||||
|
_ = s.clearRefreshToken(ctx, id)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SysUserService) Delete(ctx context.Context, id int32) error {
|
func (s *SysUserService) Delete(ctx context.Context, id int32) error {
|
||||||
@@ -282,9 +305,28 @@ func (s *SysUserService) Delete(ctx context.Context, id int32) error {
|
|||||||
return errs.ErrCannotDeleteSuperAdmin
|
return errs.ErrCannotDeleteSuperAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理缓存
|
err := s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
s.cache.ClearSysUserCache(id)
|
// 清理用户角色关联数据
|
||||||
|
err := q.ClearSysUserRoles(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
rows, err := s.queries.DeleteSysUser(ctx, id)
|
// 删除用户
|
||||||
|
rows, err := q.DeleteSysUser(ctx, id)
|
||||||
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
return dberr.MapRowsAffected(rows, err, errs.ErrUserNotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, errs.ErrUserNotFound) {
|
||||||
|
// 用户不存在也执行清理缓存操作
|
||||||
|
_ = s.clearUserCache(ctx, id)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事务执行成功后清理缓存
|
||||||
|
_ = s.clearUserCache(ctx, id)
|
||||||
|
_ = s.clearRefreshToken(ctx, id)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
87
internal/service/tag.go
Normal file
87
internal/service/tag.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"server/internal/db"
|
||||||
|
"server/internal/db/sqlc"
|
||||||
|
"server/internal/model/common"
|
||||||
|
"server/internal/model/request"
|
||||||
|
"server/internal/pkg/dberr"
|
||||||
|
"server/internal/pkg/errs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TagService struct {
|
||||||
|
store *db.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTagService(store *db.Store) *TagService {
|
||||||
|
return &TagService{
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TagService) ListPage(ctx context.Context, p *common.Pagination) ([]sqlc.Tag, int64, error) {
|
||||||
|
params := sqlc.ListTagsParams{
|
||||||
|
Limit: p.PageSize,
|
||||||
|
Offset: (p.Page - 1) * p.PageSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := s.store.CountTags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := s.store.ListTags(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TagService) ListAll(ctx context.Context) ([]sqlc.Tag, error) {
|
||||||
|
return s.store.ListAllTags(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TagService) Create(ctx context.Context, req request.CreateTagRequest) error {
|
||||||
|
params := sqlc.CreateTagParams{
|
||||||
|
Name: req.Name,
|
||||||
|
Code: req.Code,
|
||||||
|
Sort: req.Sort,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.store.CreateTag(ctx, params)
|
||||||
|
return dberr.MapUniqueViolation(err, dberr.TagCodeKey, errs.ErrTagCodeAlreadyExists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TagService) Update(ctx context.Context, id int32, req request.UpdateTagRequest) error {
|
||||||
|
params := sqlc.UpdateTagParams{
|
||||||
|
ID: id,
|
||||||
|
Name: req.Name,
|
||||||
|
Code: req.Code,
|
||||||
|
Sort: req.Sort,
|
||||||
|
}
|
||||||
|
rows, err := s.store.UpdateTag(ctx, params)
|
||||||
|
// 先判断数据条目是否存在
|
||||||
|
if err = dberr.MapRowsAffected(rows, err, errs.ErrTagNotFound); err != nil {
|
||||||
|
// 再判断code是否重复
|
||||||
|
return dberr.MapUniqueViolation(err, dberr.TagCodeKey, errs.ErrTagCodeAlreadyExists)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TagService) Delete(ctx context.Context, id int32) error {
|
||||||
|
return s.store.WithTx(ctx, func(q *sqlc.Queries) error {
|
||||||
|
// 删除中间表数据
|
||||||
|
err := s.store.DeletePostTagByTagID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除标签数据
|
||||||
|
rows, err := s.store.DeleteTag(ctx, id)
|
||||||
|
return dberr.MapRowsAffected(rows, err, errs.ErrTagNotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ sql:
|
|||||||
schema: "./internal/db/schema/*"
|
schema: "./internal/db/schema/*"
|
||||||
gen:
|
gen:
|
||||||
go:
|
go:
|
||||||
package: "db"
|
package: "sqlc"
|
||||||
out: "./internal/db/sqlc"
|
out: "./internal/db/sqlc"
|
||||||
emit_empty_slices: true
|
emit_empty_slices: true
|
||||||
emit_interface: false
|
emit_interface: false
|
||||||
|
|||||||
Reference in New Issue
Block a user