sqlx
This commit is contained in:
parent
b171122a32
commit
0878a4e6de
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,3 +33,4 @@ go.work
|
||||
!sqlc.yaml
|
||||
|
||||
public/
|
||||
example/
|
||||
|
||||
6
Makefile
6
Makefile
@ -54,4 +54,8 @@ test:
|
||||
|
||||
.PHONY: ab
|
||||
ab:
|
||||
ab -c 200 -n 5000 -H "Cookie: token=7a-zLwSNZmSF8LnMG-qC90I_pjs89tyaK_XR8HYBMZA" http://localhost:9001/system/role/list
|
||||
ab -c 200 -n 5000 -H "Cookie: token=5Qvqa6W9tJYUhTe3SHZZ828QExSzkAl9yAs_AX3rfm8" http://localhost:9001/system/role/list
|
||||
|
||||
.PHONY: asynqmon
|
||||
asynqmon:
|
||||
asynqmon --redis-addr=127.0.0.1:6379 --redis-db=2 --redis-password=secret --port=8081
|
||||
|
||||
@ -6,35 +6,44 @@ import (
|
||||
"management/internal/erpserver/handler"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/erpserver/repository/seed"
|
||||
"management/internal/erpserver/repository/system"
|
||||
"management/internal/erpserver/repository/system/audit"
|
||||
config1 "management/internal/erpserver/repository/system/config"
|
||||
"management/internal/erpserver/repository/system/department"
|
||||
"management/internal/erpserver/repository/system/loginlog"
|
||||
"management/internal/erpserver/repository/system/menu"
|
||||
"management/internal/erpserver/repository/system/role"
|
||||
"management/internal/erpserver/repository/system/rolemenu"
|
||||
"management/internal/erpserver/repository/system/user"
|
||||
v1 "management/internal/erpserver/service/v1"
|
||||
"management/internal/erpserver/service/v1/common"
|
||||
system2 "management/internal/erpserver/service/v1/system"
|
||||
"management/internal/pkg/cache"
|
||||
"management/internal/pkg/config"
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/pkg/sqldb"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Initialize(conf *config.Config, log *logger.Logger) (http.Handler, func(), error) {
|
||||
db, cleanup, err := repository.NewDB(conf, log)
|
||||
db, dbClose, err := sqldb.NewDB(conf, log)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rd, cleanup2, err := cache.ConnectRedis(conf, log)
|
||||
rd, rdClose, err := cache.ConnectRedis(conf, log)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
dbClose()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sm, err := session.NewSCSManager(rd, conf)
|
||||
if err != nil {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
dbClose()
|
||||
rdClose()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@ -43,45 +52,55 @@ func Initialize(conf *config.Config, log *logger.Logger) (http.Handler, func(),
|
||||
// =================================================================================================================
|
||||
// repo
|
||||
|
||||
repo := repository.NewRepository(db, log)
|
||||
transaction := repository.NewTransaction(repo)
|
||||
store := repository.NewStore(db)
|
||||
|
||||
userRepository := system.NewUserRepository(repo)
|
||||
roleRepository := system.NewRoleRepository(repo)
|
||||
menuRepository := system.NewMenuRepository(repo)
|
||||
roleMenuRepository := system.NewRoleMenuRepository(repo)
|
||||
departmentRepository := system.NewDepartmentRepository(repo)
|
||||
configRepository := system.NewConfigRepository(repo)
|
||||
loginLogRepository := system.NewLoginLogRepository(repo)
|
||||
auditLogRepository := system.NewAuditLogRepository(repo)
|
||||
userRepository := user.NewStore(store, log)
|
||||
roleRepository := role.NewStore(store, log)
|
||||
menuRepository := menu.NewStore(store, log)
|
||||
roleMenuRepository := rolemenu.NewStore(store, log)
|
||||
departmentRepository := department.NewStore(store, log)
|
||||
configRepository := config1.NewStore(store, log)
|
||||
loginLogRepository := loginlog.NewStore(store, log)
|
||||
auditLogRepository := audit.NewStore(store, log)
|
||||
|
||||
sd := seed.NewSeed(configRepository, departmentRepository, roleRepository, userRepository, menuRepository)
|
||||
if err := sd.Run(); err != nil {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
dbClose()
|
||||
rdClose()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// =================================================================================================================
|
||||
// service
|
||||
|
||||
service := v1.NewService(log, transaction, sm, redisCache)
|
||||
service := v1.NewService(log, sm, redisCache)
|
||||
|
||||
loginLogService := system2.NewLoginLogService(service, loginLogRepository)
|
||||
roleMenuService := system2.NewRoleMenuService(service, roleMenuRepository)
|
||||
roleService := system2.NewRoleService(service, roleRepository)
|
||||
userService := system2.NewUserService(service, userRepository, roleService, loginLogService)
|
||||
menuService := system2.NewMenuService(service, menuRepository, roleService, roleMenuService)
|
||||
departmentService := system2.NewDepartmentService(service, departmentRepository)
|
||||
userService := system2.NewUserService(service, userRepository, roleService, departmentService, loginLogService)
|
||||
menuService := system2.NewMenuService(service, menuRepository, roleService, roleMenuService)
|
||||
configService := system2.NewConfigService(service, configRepository)
|
||||
captchaService := common.NewCaptchaService()
|
||||
auditLogService := system2.NewAuditLogService(service, auditLogRepository)
|
||||
|
||||
// =================================================================================================================
|
||||
// mid
|
||||
// task
|
||||
|
||||
// 初始化审计缓冲器
|
||||
mid.InitAuditBuffer(auditLogService, log)
|
||||
opt := tasks.NewRedisClientConnector(rd)
|
||||
|
||||
taskDistributor := tasks.NewRedisTaskDistributor(log, opt)
|
||||
|
||||
taskProcessor := tasks.NewRedisTaskProcessor(log, opt, auditLogService)
|
||||
|
||||
go func() {
|
||||
log.Info("start task processor")
|
||||
err := taskProcessor.Start()
|
||||
if err != nil {
|
||||
log.Fatal("failed to start task processor", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
// =================================================================================================================
|
||||
// render
|
||||
@ -96,6 +115,7 @@ func Initialize(conf *config.Config, log *logger.Logger) (http.Handler, func(),
|
||||
Log: log,
|
||||
Sm: sm,
|
||||
Render: rdr,
|
||||
TaskDistributor: taskDistributor,
|
||||
CaptchaService: captchaService,
|
||||
UserService: userService,
|
||||
RoleService: roleService,
|
||||
@ -107,7 +127,7 @@ func Initialize(conf *config.Config, log *logger.Logger) (http.Handler, func(),
|
||||
}
|
||||
app := handler.WebApp(cfg)
|
||||
return app, func() {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
taskProcessor.Shutdown() // 复用 redis,只需要关闭一个 redis 的关闭就不需要了
|
||||
dbClose()
|
||||
}, nil
|
||||
}
|
||||
|
||||
8
go.mod
8
go.mod
@ -13,9 +13,10 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/google/wire v0.6.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/hibiken/asynq v0.25.1
|
||||
github.com/jackc/pgx/v5 v5.7.5
|
||||
github.com/jmoiron/sqlx v1.4.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/justinas/nosurf v1.2.0
|
||||
github.com/matoous/go-nanoid/v2 v2.1.0
|
||||
@ -49,10 +50,11 @@ require (
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
@ -64,6 +66,8 @@ require (
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.6.0 // indirect
|
||||
|
||||
30
go.sum
30
go.sum
@ -40,6 +40,7 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
@ -50,19 +51,17 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw=
|
||||
github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
@ -77,6 +76,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
@ -89,15 +90,16 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
|
||||
github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
|
||||
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg=
|
||||
@ -112,6 +114,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
|
||||
github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@ -152,7 +156,6 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
@ -163,7 +166,6 @@ golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -172,7 +174,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
@ -194,7 +195,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
@ -205,7 +205,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -219,14 +218,17 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@ -20,6 +20,7 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@ -31,6 +32,7 @@ type Config struct {
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
CaptchaService v1.CaptchaService
|
||||
UserService v1.UserService
|
||||
RoleService v1.RoleService
|
||||
@ -56,8 +58,8 @@ func WebApp(cfg Config) http.Handler {
|
||||
app.Handle("/public/*", http.StripPrefix("/public", uploadServer))
|
||||
|
||||
app.Group(func(r chi.Router) {
|
||||
r.Use(mid.NoSurf) // CSRF
|
||||
r.Use(mid.NoSurfContext) // CSRF Store Context
|
||||
//r.Use(mid.NoSurf) // CSRF
|
||||
//r.Use(mid.NoSurfContext) // CSRF Store Context
|
||||
r.Use(mid.LoadSession(cfg.Sm)) // Session
|
||||
|
||||
captcha.Routes(r, captcha.Config{
|
||||
@ -67,11 +69,12 @@ func WebApp(cfg Config) http.Handler {
|
||||
})
|
||||
|
||||
upload.Routes(r, upload.Config{
|
||||
Conf: cfg.Conf,
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
MenuService: cfg.MenuService,
|
||||
Conf: cfg.Conf,
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
TaskDistributor: cfg.TaskDistributor,
|
||||
MenuService: cfg.MenuService,
|
||||
})
|
||||
|
||||
home.Routes(r, home.Config{
|
||||
@ -92,13 +95,14 @@ func WebApp(cfg Config) http.Handler {
|
||||
})
|
||||
|
||||
r.Route("/system", func(r chi.Router) {
|
||||
|
||||
|
||||
r.Use(mid.Authorize(cfg.Sm, cfg.MenuService))
|
||||
|
||||
user.Routes(r, user.Config{
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
TaskDistributor: cfg.TaskDistributor,
|
||||
UserService: cfg.UserService,
|
||||
RoleService: cfg.RoleService,
|
||||
MenuService: cfg.MenuService,
|
||||
@ -106,18 +110,20 @@ func WebApp(cfg Config) http.Handler {
|
||||
})
|
||||
|
||||
role.Routes(r, role.Config{
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
RoleService: cfg.RoleService,
|
||||
MenuService: cfg.MenuService,
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
TaskDistributor: cfg.TaskDistributor,
|
||||
RoleService: cfg.RoleService,
|
||||
MenuService: cfg.MenuService,
|
||||
})
|
||||
|
||||
menu.Routes(r, menu.Config{
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
MenuService: cfg.MenuService,
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
TaskDistributor: cfg.TaskDistributor,
|
||||
MenuService: cfg.MenuService,
|
||||
})
|
||||
|
||||
department.Routes(r, department.Config{
|
||||
@ -129,11 +135,12 @@ func WebApp(cfg Config) http.Handler {
|
||||
})
|
||||
|
||||
configapp.Routes(r, configapp.Config{
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
MenuService: cfg.MenuService,
|
||||
ConfigService: cfg.ConfigService,
|
||||
Log: cfg.Log,
|
||||
Sm: cfg.Sm,
|
||||
Render: cfg.Render,
|
||||
TaskDistributor: cfg.TaskDistributor,
|
||||
MenuService: cfg.MenuService,
|
||||
ConfigService: cfg.ConfigService,
|
||||
})
|
||||
|
||||
loginlog.Routes(r, loginlog.Config{
|
||||
|
||||
@ -5,17 +5,19 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
MenuService v1.MenuService
|
||||
ConfigService v1.ConfigService
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
MenuService v1.MenuService
|
||||
ConfigService v1.ConfigService
|
||||
}
|
||||
|
||||
func Routes(r chi.Router, cfg Config) {
|
||||
@ -23,7 +25,7 @@ func Routes(r chi.Router, cfg Config) {
|
||||
|
||||
r.Get("/pear.json", app.pear)
|
||||
r.Route("/config", func(r chi.Router) {
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log))
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log, cfg.TaskDistributor))
|
||||
r.Get("/list", app.list)
|
||||
r.Post("/list", app.list)
|
||||
r.Get("/add", app.add)
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@ -14,6 +15,7 @@ type Config struct {
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
MenuService v1.MenuService
|
||||
DepartmentService v1.DepartmentService
|
||||
}
|
||||
@ -22,7 +24,7 @@ func Routes(r chi.Router, cfg Config) {
|
||||
app := newApp(cfg.Render, cfg.DepartmentService)
|
||||
|
||||
r.Route("/department", func(r chi.Router) {
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log))
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log, cfg.TaskDistributor))
|
||||
r.Get("/list", app.list)
|
||||
r.Post("/list", app.list)
|
||||
r.Get("/add", app.add)
|
||||
|
||||
@ -5,16 +5,18 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
MenuService v1.MenuService
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
MenuService v1.MenuService
|
||||
}
|
||||
|
||||
func Routes(r chi.Router, cfg Config) {
|
||||
@ -22,7 +24,7 @@ func Routes(r chi.Router, cfg Config) {
|
||||
|
||||
r.Get("/menus", app.menus)
|
||||
r.Route("/menu", func(r chi.Router) {
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log))
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log, cfg.TaskDistributor))
|
||||
r.Get("/list", app.list)
|
||||
r.Post("/list", app.list)
|
||||
r.Get("/add", app.add)
|
||||
|
||||
@ -5,24 +5,26 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
RoleService v1.RoleService
|
||||
MenuService v1.MenuService
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
RoleService v1.RoleService
|
||||
MenuService v1.MenuService
|
||||
}
|
||||
|
||||
func Routes(r chi.Router, cfg Config) {
|
||||
app := newApp(cfg.Render, cfg.RoleService, cfg.MenuService)
|
||||
|
||||
r.Route("/role", func(r chi.Router) {
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log))
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log, cfg.TaskDistributor))
|
||||
r.Get("/list", app.list)
|
||||
r.Post("/list", app.list)
|
||||
r.Get("/add", app.add)
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@ -14,6 +15,7 @@ type Config struct {
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
UserService v1.UserService
|
||||
RoleService v1.RoleService
|
||||
MenuService v1.MenuService
|
||||
@ -31,7 +33,7 @@ func Routes(r chi.Router, cfg Config) {
|
||||
)
|
||||
|
||||
r.Route("/user", func(r chi.Router) {
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log))
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log, cfg.TaskDistributor))
|
||||
r.Get("/list", app.list)
|
||||
r.Post("/list", app.list)
|
||||
r.Get("/add", app.add)
|
||||
|
||||
@ -6,17 +6,19 @@ import (
|
||||
"management/internal/pkg/mid"
|
||||
"management/internal/pkg/render"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Conf *config.Config
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
MenuService v1.MenuService
|
||||
Conf *config.Config
|
||||
Log *logger.Logger
|
||||
Sm session.Manager
|
||||
Render render.Renderer
|
||||
TaskDistributor tasks.TaskDistributor
|
||||
MenuService v1.MenuService
|
||||
}
|
||||
|
||||
func Routes(r chi.Router, cfg Config) {
|
||||
@ -24,9 +26,9 @@ func Routes(r chi.Router, cfg Config) {
|
||||
|
||||
r.Route("/upload", func(r chi.Router) {
|
||||
r.Use(mid.Authorize(cfg.Sm, cfg.MenuService))
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log))
|
||||
r.Get("/img", app.img)
|
||||
r.Get("/file", app.file)
|
||||
r.Get("/multi_files", app.multiFiles)
|
||||
r.Use(mid.Audit(cfg.Sm, cfg.Log, cfg.TaskDistributor))
|
||||
r.Post("/img", app.img)
|
||||
r.Post("/file", app.file)
|
||||
r.Post("/multi_files", app.multiFiles)
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,28 +13,25 @@ import (
|
||||
type AuditLogRepository interface {
|
||||
Create(ctx context.Context, obj *AuditLog) error
|
||||
BatchCreate(ctx context.Context, objs []*AuditLog) error
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*AuditLog, int64, error)
|
||||
Count(ctx context.Context, filter dto.SearchDto) (int64, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*AuditLog, error)
|
||||
}
|
||||
|
||||
type AuditLog struct {
|
||||
ID int64 `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Email string `json:"email"`
|
||||
StartAt time.Time `json:"start_at"`
|
||||
EndAt time.Time `json:"end_at"`
|
||||
Duration string `json:"duration"`
|
||||
Url string `json:"url"`
|
||||
Method string `json:"method"`
|
||||
Parameters string `json:"parameters"`
|
||||
RefererUrl string `json:"referer_url"`
|
||||
Os string `json:"os"`
|
||||
Ip string `json:"ip"`
|
||||
Browser string `json:"browser"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func (AuditLog) TableName() string {
|
||||
return "sys_audit_log"
|
||||
ID int64 `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Email string `db:"email" json:"email"`
|
||||
StartAt time.Time `db:"start_at" json:"start_at"`
|
||||
EndAt time.Time `db:"end_at" json:"end_at"`
|
||||
Duration string `db:"duration" json:"duration"`
|
||||
Url string `db:"url" json:"url"`
|
||||
Method string `db:"method" json:"method"`
|
||||
Parameters string `db:"parameters" json:"parameters"`
|
||||
RefererUrl string `db:"referer_url" json:"referer_url"`
|
||||
Os string `db:"os" json:"os"`
|
||||
Ip string `db:"ip" json:"ip"`
|
||||
Browser string `db:"browser" json:"browser"`
|
||||
Remark string `db:"remark" json:"remark"`
|
||||
}
|
||||
|
||||
func NewAuditLog(r *http.Request, email, os, browser string, start, end time.Time) *AuditLog {
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
package system
|
||||
|
||||
type Category struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
Name string `json:"name" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
}
|
||||
|
||||
func (Category) TableName() string {
|
||||
return "categories"
|
||||
}
|
||||
@ -15,18 +15,14 @@ type ConfigRepository interface {
|
||||
Update(ctx context.Context, obj *Config) error
|
||||
Get(ctx context.Context, id int32) (*Config, error)
|
||||
GetByKey(ctx context.Context, key string) (*Config, error)
|
||||
GetValueByKey(ctx context.Context, key string) ([]byte, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*Config, int64, error)
|
||||
Count(ctx context.Context, filter dto.SearchDto) (int64, error)
|
||||
List(ctx context.Context, filter dto.SearchDto) ([]*Config, error)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
Key string `json:"key" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
Value datatypes.JSON `json:"value" gorm:"type:jsonb;not null;"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"type:timestamptz;not null;default:'now()'"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamptz;not null;default:'0001-01-01 00:00:00+8';"`
|
||||
}
|
||||
|
||||
func (Config) TableName() string {
|
||||
return "sys_config"
|
||||
ID int32 `db:"id" json:"id"`
|
||||
Key string `db:"key" json:"key"`
|
||||
Value datatypes.JSON `db:"value" json:"value"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
@ -13,21 +13,18 @@ type DepartmentRepository interface {
|
||||
Update(ctx context.Context, obj *Department) error
|
||||
Get(ctx context.Context, id int32) (*Department, error)
|
||||
All(ctx context.Context) ([]*Department, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*Department, int64, error)
|
||||
Count(ctx context.Context, filter dto.SearchDto) (int64, error)
|
||||
List(ctx context.Context, filter dto.SearchDto) ([]*Department, error)
|
||||
RebuildParentPath(ctx context.Context) error
|
||||
}
|
||||
|
||||
type Department struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
Name string `json:"name" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
ParentID int32 `json:"parent_id" gorm:"type:int;not null;"`
|
||||
ParentPath string `json:"parent_path" gorm:"type:varchar(500);not null;"`
|
||||
Status int32 `json:"status" gorm:"type:int;not null;default:0;"`
|
||||
Sort int32 `json:"sort" gorm:"type:int;not null;default:0;"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"type:timestamptz;not null;default:'now()'"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamptz;not null;default:'0001-01-01 00:00:00+8';"`
|
||||
}
|
||||
|
||||
func (Department) TableName() string {
|
||||
return "sys_department"
|
||||
ID int32 `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
ParentID int32 `db:"parent_id" json:"parent_id"`
|
||||
ParentPath string `db:"parent_path" json:"parent_path"`
|
||||
Status int32 `db:"status" json:"status"`
|
||||
Sort int32 `db:"sort" json:"sort"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
@ -10,25 +10,21 @@ import (
|
||||
type LoginLogRepository interface {
|
||||
Create(ctx context.Context, obj *LoginLog) error
|
||||
GetLatest(ctx context.Context, email string) ([]*LoginLog, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*LoginLog, int64, error)
|
||||
Count(ctx context.Context, email string) (int64, error)
|
||||
Count(ctx context.Context, filter dto.SearchDto) (int64, error)
|
||||
List(ctx context.Context, filter dto.SearchDto) ([]*LoginLog, error)
|
||||
}
|
||||
|
||||
type LoginLog struct {
|
||||
ID int64 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"type:timestamptz;not null;default:'now()'"`
|
||||
Email string `json:"email" gorm:"type:varchar(100);not null;"`
|
||||
IsSuccess bool `json:"is_success" gorm:"type:boolean;not null;"`
|
||||
Message string `json:"message" gorm:"type:varchar(300);not null;"`
|
||||
RefererUrl string `json:"referer_url" gorm:"type:varchar(500);not null;"`
|
||||
Url string `json:"url" gorm:"type:varchar(500);not null;"`
|
||||
Os string `json:"os" gorm:"type:varchar(50);not null;"`
|
||||
Ip string `json:"ip" gorm:"type:varchar(20);not null;"`
|
||||
Browser string `json:"browser" gorm:"type:varchar(100);not null;"`
|
||||
}
|
||||
|
||||
func (*LoginLog) TableName() string {
|
||||
return "sys_user_login_log"
|
||||
ID int64 `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Email string `db:"email" json:"email"`
|
||||
IsSuccess bool `db:"is_success" json:"is_success"`
|
||||
Message string `db:"message" json:"message"`
|
||||
RefererUrl string `db:"referer_url" json:"referer_url"`
|
||||
Url string `db:"url" json:"url"`
|
||||
Os string `db:"os" json:"os"`
|
||||
Ip string `db:"ip" json:"ip"`
|
||||
Browser string `db:"browser" json:"browser"`
|
||||
}
|
||||
|
||||
func NewLoginLog(email, os, ip, browser, url, referer string) *LoginLog {
|
||||
|
||||
@ -12,27 +12,24 @@ type MenuRepository interface {
|
||||
Get(ctx context.Context, id int32) (*Menu, error)
|
||||
GetByUrl(ctx context.Context, url string) (*Menu, error)
|
||||
All(ctx context.Context) ([]*Menu, error)
|
||||
Count(ctx context.Context) (int64, error)
|
||||
RebuildParentPath(ctx context.Context) error
|
||||
}
|
||||
|
||||
type Menu struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
Name string `json:"name" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
DisplayName string `json:"display_name" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
Url string `json:"url" gorm:"type:varchar(200);not null;"`
|
||||
Type string `json:"type" gorm:"type:varchar(50);not null;"`
|
||||
ParentID int32 `json:"parent_id" gorm:"type:int;not null;"`
|
||||
ParentPath string `json:"parent_path" gorm:"type:varchar(500);not null;"`
|
||||
Avatar string `json:"avatar" gorm:"type:varchar(100);not null;"`
|
||||
Style string `json:"style" gorm:"type:varchar(100);not null;"`
|
||||
Visible bool `json:"visible" gorm:"type:boolean;not null;"`
|
||||
IsList bool `json:"is_list" gorm:"type:boolean;not null;"`
|
||||
Status int32 `json:"status" gorm:"type:int;not null;default:0;"`
|
||||
Sort int32 `json:"sort" gorm:"type:int;not null;default:0;"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"type:timestamptz;not null;default:'now()'"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamptz;not null;default:'0001-01-01 00:00:00+8';"`
|
||||
}
|
||||
|
||||
func (Menu) TableName() string {
|
||||
return "sys_menu"
|
||||
ID int32 `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
Url string `db:"url" json:"url"`
|
||||
Type string `db:"type" json:"type"`
|
||||
ParentID int32 `db:"parent_id" json:"parent_id"`
|
||||
ParentPath string `db:"parent_path" json:"parent_path"`
|
||||
Avatar string `db:"avatar" json:"avatar"`
|
||||
Style string `db:"style" json:"style"`
|
||||
Visible bool `db:"visible" json:"visible"`
|
||||
IsList bool `db:"is_list" json:"is_list"`
|
||||
Status int32 `db:"status" json:"status"`
|
||||
Sort int32 `db:"sort" json:"sort"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
@ -12,24 +12,22 @@ type RoleRepository interface {
|
||||
Create(ctx context.Context, obj *Role) error
|
||||
Update(ctx context.Context, obj *Role) error
|
||||
Get(ctx context.Context, id int32) (*Role, error)
|
||||
GetByVip(ctx context.Context, vip bool) (*Role, error)
|
||||
All(ctx context.Context) ([]*Role, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*Role, int64, error)
|
||||
Count(ctx context.Context, filter dto.SearchDto) (int64, error)
|
||||
List(ctx context.Context, filter dto.SearchDto) ([]*Role, error)
|
||||
RebuildParentPath(ctx context.Context) error
|
||||
}
|
||||
|
||||
type Role struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
Name string `json:"name" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
DisplayName string `json:"display_name" gorm:"type:varchar(200);not null;uniqueIndex"`
|
||||
ParentID int32 `json:"parent_id" gorm:"type:int;not null;"`
|
||||
ParentPath string `json:"parent_path" gorm:"type:varchar(500);not null;"`
|
||||
Vip bool `json:"-" gorm:"type:boolean;not null;"`
|
||||
Status int32 `json:"status" gorm:"type:int;not null;default:0;"`
|
||||
Sort int32 `json:"sort" gorm:"type:int;not null;default:0;"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"type:timestamptz;not null;default:'now()'"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamptz;not null;default:'0001-01-01 00:00:00+8';"`
|
||||
}
|
||||
|
||||
func (Role) TableName() string {
|
||||
return "sys_role"
|
||||
ID int32 `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DisplayName string `db:"display_name" json:"display_name"`
|
||||
ParentID int32 `db:"parent_id" json:"parent_id"`
|
||||
ParentPath string `db:"parent_path" json:"parent_path"`
|
||||
Vip bool `db:"vip" json:"-"`
|
||||
Status int32 `db:"status" json:"status"`
|
||||
Sort int32 `db:"sort" json:"sort"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
@ -9,10 +9,6 @@ type RoleMenuRepository interface {
|
||||
}
|
||||
|
||||
type RoleMenu struct {
|
||||
RoleID int32 `json:"role_id" gorm:"primaryKey;autoIncrement:false;type:int;not null;"`
|
||||
MenuID int32 `json:"menu_id" gorm:"primaryKey;autoIncrement:false;type:int;not null;"`
|
||||
}
|
||||
|
||||
func (RoleMenu) TableName() string {
|
||||
return "sys_role_menu"
|
||||
RoleID int32 `db:"role_id" json:"role_id"`
|
||||
MenuID int32 `db:"menu_id" json:"menu_id"`
|
||||
}
|
||||
|
||||
@ -16,29 +16,26 @@ type UserRepository interface {
|
||||
Get(ctx context.Context, id int32) (*User, error)
|
||||
GetByEmail(ctx context.Context, email string) (*User, error)
|
||||
All(ctx context.Context) ([]*User, error)
|
||||
List(ctx context.Context, q dto.SearchDto) ([]*User, int64, error)
|
||||
Count(ctx context.Context, filter dto.SearchDto) (int64, error)
|
||||
List(ctx context.Context, filter dto.SearchDto) ([]*User, error)
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"`
|
||||
Uuid uuid.UUID `json:"uuid" gorm:"type:uuid;not null;uniqueIndex"`
|
||||
Email string `json:"email" gorm:"type:varchar(100);not null;uniqueIndex"`
|
||||
Username string `json:"username" gorm:"type:varchar(100);not null;uniqueIndex"`
|
||||
HashedPassword []byte `json:"-" gorm:"type:bytea;not null;"`
|
||||
Salt string `json:"-" gorm:"type:varchar(20);not null;"`
|
||||
Avatar string `json:"avatar" gorm:"type:varchar(200);not null;"`
|
||||
Gender int32 `json:"gender" gorm:"type:int;not null;default:0;"`
|
||||
DepartmentID int32 `json:"department_id" gorm:"type:int;not null;default:0;"`
|
||||
RoleID int32 `json:"role_id" gorm:"type:int;not null;default:0;"`
|
||||
Status int32 `json:"status" gorm:"type:int;not null;default:0;"`
|
||||
ChangePasswordAt time.Time `json:"-" gorm:"type:timestamptz;not null;default:'0001-01-01 00:00:00+8';"`
|
||||
CreatedAt time.Time `json:"created_at" gorm:"type:timestamptz;not null;default:'now()'"`
|
||||
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamptz;not null;default:'0001-01-01 00:00:00+8';"`
|
||||
ID int32 `db:"id" json:"id"`
|
||||
Uuid uuid.UUID `db:"uuid" json:"uuid"`
|
||||
Email string `db:"email" json:"email"`
|
||||
Username string `db:"username" json:"username"`
|
||||
HashedPassword []byte `db:"hashed_password" json:"-"`
|
||||
Salt string `db:"salt" json:"-"`
|
||||
Avatar string `db:"avatar" json:"avatar"`
|
||||
Gender int32 `db:"gender" json:"gender"`
|
||||
DepartmentID int32 `db:"department_id" json:"department_id"`
|
||||
RoleID int32 `db:"role_id" json:"role_id"`
|
||||
Status int32 `db:"status" json:"status"`
|
||||
ChangePasswordAt time.Time `db:"change_password_at" json:"-"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
|
||||
Role *Role `json:"role" gorm:"ForeignKey:RoleID"`
|
||||
Department *Department `json:"department" gorm:"ForeignKey:DepartmentID"`
|
||||
}
|
||||
|
||||
func (User) TableName() string {
|
||||
return "sys_user"
|
||||
Role *Role `json:"role"`
|
||||
Department *Department `json:"department"`
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- SQL dump generated using DBML (dbml.dbdiagram.io)
|
||||
-- Database: PostgreSQL
|
||||
-- Generated at: 2025-06-12T01:21:05.629Z
|
||||
-- Generated at: 2025-06-18T02:08:22.726Z
|
||||
|
||||
CREATE TABLE "sys_user" (
|
||||
"id" SERIAL NOT NULL,
|
||||
|
||||
@ -2,163 +2,55 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/pkg/config"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
gl "gorm.io/gorm/logger"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var core *sqlx.DB
|
||||
|
||||
type txCtxKey struct{}
|
||||
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
logger *logger.Logger
|
||||
type Store struct {
|
||||
db sqlx.ExtContext
|
||||
}
|
||||
|
||||
func NewRepository(db *gorm.DB, logger *logger.Logger) *Repository {
|
||||
return &Repository{
|
||||
db: db,
|
||||
logger: logger,
|
||||
func NewStore(db *sqlx.DB) *Store {
|
||||
if core == nil {
|
||||
core = db
|
||||
}
|
||||
return &Store{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
type Transaction interface {
|
||||
Transaction(ctx context.Context, fn func(ctx context.Context) error) error
|
||||
}
|
||||
|
||||
func NewTransaction(r *Repository) Transaction {
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Repository) DB(ctx context.Context) *gorm.DB {
|
||||
v := ctx.Value(txCtxKey{})
|
||||
if v != nil {
|
||||
if tx, ok := v.(*gorm.DB); ok {
|
||||
return tx
|
||||
func (s *Store) DB(ctx context.Context) sqlx.ExtContext {
|
||||
if tx, ok := ctx.Value(txCtxKey{}).(sqldb.CommitRollbacker); ok {
|
||||
if res, err := sqldb.GetExtContext(tx); err == nil {
|
||||
return res
|
||||
}
|
||||
}
|
||||
return r.db.WithContext(ctx)
|
||||
return s.db
|
||||
}
|
||||
|
||||
func (r *Repository) Transaction(ctx context.Context, fn func(ctx context.Context) error) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
ctx = context.WithValue(ctx, txCtxKey{}, tx)
|
||||
return fn(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func NewDB(config *config.Config, log *logger.Logger) (*gorm.DB, func(), error) {
|
||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
|
||||
config.DB.Host,
|
||||
config.DB.Username,
|
||||
config.DB.Password,
|
||||
config.DB.DBName,
|
||||
config.DB.Port,
|
||||
)
|
||||
pgConfig := postgres.Config{
|
||||
DSN: dsn,
|
||||
PreferSimpleProtocol: true, // disables implicit prepared statement usage
|
||||
}
|
||||
db, err := gorm.Open(postgres.New(pgConfig), &gorm.Config{
|
||||
Logger: getGormLogger(config),
|
||||
})
|
||||
func Transaction(ctx context.Context, log *logger.Logger, fn func(c context.Context) error) error {
|
||||
beginner := sqldb.NewBeginner(core)
|
||||
tx, err := beginner.Begin()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
log.Error("begin transaction error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// db.Debug 会默认显示日志
|
||||
//db = db.Debug()
|
||||
|
||||
// Connection Pool config
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := sqlDB.PingContext(ctx); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 设置最大空闲连接数(默认 2)
|
||||
sqlDB.SetMaxIdleConns(config.DB.MaxIdleConns)
|
||||
|
||||
// 设置最大打开连接数(默认 0 无限制)
|
||||
sqlDB.SetMaxOpenConns(config.DB.MaxOpenConns)
|
||||
|
||||
// 设置连接最大存活时间
|
||||
sqlDB.SetConnMaxLifetime(config.DB.ConnMaxLifetime)
|
||||
|
||||
// 设置连接最大空闲时间
|
||||
sqlDB.SetConnMaxIdleTime(config.DB.ConnMaxIdleTime)
|
||||
|
||||
cleanup := func() {
|
||||
if err := sqlDB.Close(); err != nil {
|
||||
log.Error("sql db close error", err)
|
||||
ctx = context.WithValue(ctx, txCtxKey{}, tx)
|
||||
if err := fn(ctx); err != nil {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
log.Error("rollback transaction error", err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return db, cleanup, nil
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func getGormLogger(config *config.Config) gl.Interface {
|
||||
if config.DB.LogMode {
|
||||
return gl.Default.LogMode(gl.Info) // 开发环境显示日志
|
||||
}
|
||||
return gl.Default.LogMode(gl.Silent)
|
||||
}
|
||||
|
||||
//var (
|
||||
// once sync.Once
|
||||
// // 全局变量,方便其它包直接调用已初始化好的 datastore 实例.
|
||||
// engine *datastore
|
||||
//)
|
||||
//
|
||||
//type Store interface {
|
||||
// DB(ctx context.Context) *gorm.DB
|
||||
// TX(ctx context.Context, fn func(ctx context.Context) error) error
|
||||
//}
|
||||
//
|
||||
//// transactionKey 用于在 context.Context 中存储事务上下文的键.
|
||||
//type transactionKey struct{}
|
||||
//
|
||||
//// datastore 是 Store 的具体实现.
|
||||
//type datastore struct {
|
||||
// core *gorm.DB
|
||||
//}
|
||||
//
|
||||
//// NewStore 创建一个 Store 类型的实例.
|
||||
//func NewStore(db *gorm.DB) Store {
|
||||
// // 确保 engine 只被初始化一次
|
||||
// once.Do(func() {
|
||||
// engine = &datastore{db}
|
||||
// })
|
||||
//
|
||||
// return engine
|
||||
//}
|
||||
//
|
||||
//func (store *datastore) DB(ctx context.Context) *gorm.DB {
|
||||
// db := store.core
|
||||
// // 从上下文中提取事务实例
|
||||
// if tx, ok := ctx.Value(transactionKey{}).(*gorm.DB); ok {
|
||||
// db = tx
|
||||
// }
|
||||
//
|
||||
// return db
|
||||
//}
|
||||
//
|
||||
//func (store *datastore) TX(ctx context.Context, fn func(ctx context.Context) error) error {
|
||||
// return store.core.WithContext(ctx).Transaction(
|
||||
// func(tx *gorm.DB) error {
|
||||
// ctx = context.WithValue(ctx, transactionKey{}, tx)
|
||||
// return fn(ctx)
|
||||
// },
|
||||
// )
|
||||
//}
|
||||
|
||||
117
internal/erpserver/repository/system/audit/audit.go
Normal file
117
internal/erpserver/repository/system/audit/audit.go
Normal file
@ -0,0 +1,117 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.AuditLogRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.AuditLog) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_audit_log(
|
||||
email, start_at, end_at, duration, url, method, parameters,
|
||||
referer_url, os, ip, browser, remark)
|
||||
VALUES (
|
||||
:email, :start_at, :end_at, :duration, :url, :method, :parameters,
|
||||
:referer_url, :os, :ip, :browser, :remark)`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) BatchCreate(ctx context.Context, objs []*system.AuditLog) error {
|
||||
if len(objs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_audit_log(
|
||||
email, start_at, end_at, duration, url, method, parameters,
|
||||
referer_url, os, ip, browser, remark)
|
||||
VALUES (
|
||||
:email, :start_at, :end_at, :duration, :url, :method, :parameters,
|
||||
:referer_url, :os, :ip, :browser, :remark)`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, objs)
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context, filter dto.SearchDto) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_audit_log`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), buf.String(), data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count audit: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
|
||||
func (s *store) List(ctx context.Context, filter dto.SearchDto) ([]*system.AuditLog, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, created_at, email, start_at, end_at, duration, url,
|
||||
method, parameters, referer_url, os, ip, browser, remark
|
||||
FROM
|
||||
sys_audit_log`
|
||||
|
||||
data := map[string]any{
|
||||
"offset": (filter.Page - 1) * filter.Rows,
|
||||
"rows_per_page": filter.Rows,
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
buf.WriteString(" ORDER BY id DESC")
|
||||
buf.WriteString(" LIMIT :rows_per_page OFFSET :offset")
|
||||
|
||||
var audits []system.AuditLog
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), buf.String(), data, &audits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(audits), nil
|
||||
}
|
||||
|
||||
func toPointer(data []system.AuditLog) []*system.AuditLog {
|
||||
var res []*system.AuditLog
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
28
internal/erpserver/repository/system/audit/filter.go
Normal file
28
internal/erpserver/repository/system/audit/filter.go
Normal file
@ -0,0 +1,28 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func applyFilter(filter dto.SearchDto, data map[string]any, buf *bytes.Buffer) {
|
||||
var wc []string
|
||||
|
||||
if filter.SearchTimeBegin != "" && filter.SearchTimeEnd == "" {
|
||||
data["start_at"] = filter.SearchTimeBegin
|
||||
data["end_at"] = filter.SearchTimeEnd
|
||||
wc = append(wc, "created_at BETWEEN :start_at AND :end_at")
|
||||
}
|
||||
|
||||
if filter.SearchEmail != "" {
|
||||
data["email"] = filter.SearchEmail
|
||||
wc = append(wc, "email LIKE :email")
|
||||
}
|
||||
|
||||
if len(wc) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteString(strings.Join(wc, " AND "))
|
||||
}
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
)
|
||||
|
||||
type auditLogRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewAuditLogRepository(repo *repository.Repository) system.AuditLogRepository {
|
||||
return &auditLogRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *auditLogRepository) Create(ctx context.Context, obj *system.AuditLog) error {
|
||||
return s.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (s *auditLogRepository) BatchCreate(ctx context.Context, objs []*system.AuditLog) error {
|
||||
return s.repo.DB(ctx).Create(objs).Error
|
||||
}
|
||||
|
||||
func (s *auditLogRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.AuditLog, int64, error) {
|
||||
query := s.repo.DB(ctx).
|
||||
Model(&system.AuditLog{}).
|
||||
Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd)
|
||||
if q.SearchEmail != "" {
|
||||
query = query.Where("email LIKE ?", "%"+q.SearchEmail+"%")
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := query.Count(&count).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var logs []*system.AuditLog
|
||||
err = query.
|
||||
Order("id DESC").
|
||||
Offset((q.Page - 1) * q.Rows).
|
||||
Limit(q.Rows).
|
||||
Find(&logs).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return logs, count, nil
|
||||
}
|
||||
@ -1,113 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/database"
|
||||
"management/internal/pkg/know/pearadmin"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type configRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewConfigRepository(repo *repository.Repository) system.ConfigRepository {
|
||||
return &configRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *configRepository) Initialize(ctx context.Context) error {
|
||||
_, err := r.GetByKey(ctx, pearadmin.PearKey)
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
b, e := json.Marshal(pearadmin.PearJson)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
s := system.Config{
|
||||
Key: pearadmin.PearKey,
|
||||
Value: datatypes.JSON(b),
|
||||
}
|
||||
return r.Create(ctx, &s)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *configRepository) Create(ctx context.Context, obj *system.Config) error {
|
||||
return r.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (r *configRepository) Update(ctx context.Context, obj *system.Config) error {
|
||||
return r.repo.DB(ctx).Save(obj).Error
|
||||
}
|
||||
|
||||
func (r *configRepository) Get(ctx context.Context, id int32) (*system.Config, error) {
|
||||
var obj system.Config
|
||||
err := r.repo.DB(ctx).First(&obj, id).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("config %d not found: %w", id, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
func (r *configRepository) GetByKey(ctx context.Context, key string) (*system.Config, error) {
|
||||
var obj system.Config
|
||||
err := r.repo.DB(ctx).Where("key = ?", key).First(&obj).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("config key %s not found: %w", key, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
func (r *configRepository) GetValueByKey(ctx context.Context, key string) ([]byte, error) {
|
||||
var obj system.Config
|
||||
err := r.repo.DB(ctx).Where("key = ?", key).First(&obj).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("config key value %s not found: %w", key, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return obj.Value, nil
|
||||
}
|
||||
|
||||
func (r *configRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.Config, int64, error) {
|
||||
query := r.repo.DB(ctx).
|
||||
Model(&system.Config{}).
|
||||
Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd)
|
||||
|
||||
var count int64
|
||||
err := query.Count(&count).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var configs []*system.Config
|
||||
err = query.
|
||||
Order("id DESC").
|
||||
Offset((q.Page - 1) * q.Rows).
|
||||
Limit(q.Rows).
|
||||
Find(&configs).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return configs, count, nil
|
||||
}
|
||||
177
internal/erpserver/repository/system/config/config.go
Normal file
177
internal/erpserver/repository/system/config/config.go
Normal file
@ -0,0 +1,177 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/database"
|
||||
"management/internal/pkg/know/pearadmin"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.ConfigRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Initialize(ctx context.Context) error {
|
||||
_, err := s.GetByKey(ctx, pearadmin.PearKey)
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
b, e := json.Marshal(pearadmin.PearJson)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
return s.Create(ctx, &system.Config{
|
||||
Key: pearadmin.PearKey,
|
||||
Value: datatypes.JSON(b),
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.Config) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_config (
|
||||
key, value
|
||||
) VALUES (
|
||||
:key, :value
|
||||
);`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Update(ctx context.Context, obj *system.Config) error {
|
||||
const q = `
|
||||
UPDATE sys_config
|
||||
SET key = :key,
|
||||
value = :value,
|
||||
updated_at = :updated_at
|
||||
WHERE id = :id;`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Get(ctx context.Context, id int32) (*system.Config, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, key, value, created_at, updated_at
|
||||
FROM
|
||||
sys_config
|
||||
WHERE
|
||||
id = :id;`
|
||||
|
||||
data := map[string]any{
|
||||
"id": id,
|
||||
}
|
||||
|
||||
var config system.Config
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select id config: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func (s *store) GetByKey(ctx context.Context, key string) (*system.Config, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, key, value, created_at, updated_at
|
||||
FROM
|
||||
sys_config
|
||||
WHERE
|
||||
key = :key;`
|
||||
|
||||
data := map[string]any{
|
||||
"key": key,
|
||||
}
|
||||
|
||||
var config system.Config
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select key config: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context, filter dto.SearchDto) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_config`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), buf.String(), data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count config: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
func (s *store) List(ctx context.Context, filter dto.SearchDto) ([]*system.Config, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, key, value, created_at, updated_at
|
||||
FROM
|
||||
sys_config`
|
||||
|
||||
data := map[string]any{
|
||||
"offset": (filter.Page - 1) * filter.Rows,
|
||||
"rows_per_page": filter.Rows,
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
buf.WriteString(" ORDER BY id DESC")
|
||||
buf.WriteString(" LIMIT :rows_per_page OFFSET :offset")
|
||||
|
||||
var configs []system.Config
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), buf.String(), data, &configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(configs), nil
|
||||
}
|
||||
|
||||
func toPointer(data []system.Config) []*system.Config {
|
||||
var res []*system.Config
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
23
internal/erpserver/repository/system/config/filter.go
Normal file
23
internal/erpserver/repository/system/config/filter.go
Normal file
@ -0,0 +1,23 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func applyFilter(filter dto.SearchDto, data map[string]any, buf *bytes.Buffer) {
|
||||
var wc []string
|
||||
|
||||
if filter.SearchTimeBegin != "" && filter.SearchTimeEnd == "" {
|
||||
data["start_at"] = filter.SearchTimeBegin
|
||||
data["end_at"] = filter.SearchTimeEnd
|
||||
wc = append(wc, "created_at BETWEEN :start_at AND :end_at")
|
||||
}
|
||||
|
||||
if len(wc) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteString(strings.Join(wc, " AND "))
|
||||
}
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/database"
|
||||
)
|
||||
|
||||
type departmentRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewDepartmentRepository(repo *repository.Repository) system.DepartmentRepository {
|
||||
return &departmentRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *departmentRepository) Initialize(ctx context.Context) error {
|
||||
var count int64
|
||||
if err := r.repo.DB(ctx).Model(&system.Department{}).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
obj := system.Department{
|
||||
Name: "公司",
|
||||
ParentID: 0,
|
||||
ParentPath: ",0,",
|
||||
Status: 0,
|
||||
Sort: 6666,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
return r.Create(ctx, &obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *departmentRepository) Create(ctx context.Context, obj *system.Department) error {
|
||||
return r.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (r *departmentRepository) Update(ctx context.Context, obj *system.Department) error {
|
||||
return r.repo.DB(ctx).Save(obj).Error
|
||||
}
|
||||
|
||||
func (r *departmentRepository) Get(ctx context.Context, id int32) (*system.Department, error) {
|
||||
var obj system.Department
|
||||
err := r.repo.DB(ctx).First(&obj, id).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("department %d not found: %w", id, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
func (r *departmentRepository) All(ctx context.Context) ([]*system.Department, error) {
|
||||
var departs []*system.Department
|
||||
err := r.repo.DB(ctx).Find(&departs).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return departs, nil
|
||||
}
|
||||
|
||||
func (r *departmentRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.Department, int64, error) {
|
||||
query := r.repo.DB(ctx).
|
||||
Model(&system.Department{}).
|
||||
Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd)
|
||||
if q.SearchID != 0 {
|
||||
query = query.Where("id = ?", q.SearchID)
|
||||
}
|
||||
if q.SearchParentID != 0 && q.SearchParentID != 1 {
|
||||
query = query.Where("parent_id = ?", q.SearchParentID)
|
||||
}
|
||||
if q.SearchName != "" {
|
||||
query = query.Where("name LIKE ?", "%"+q.SearchName+"%")
|
||||
}
|
||||
if q.SearchStatus != 9999 {
|
||||
query = query.Where("status = ?", q.SearchStatus)
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := query.Count(&count).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var departs []*system.Department
|
||||
err = query.
|
||||
Order("id DESC").
|
||||
Offset((q.Page - 1) * q.Rows).
|
||||
Limit(q.Rows).
|
||||
Find(&departs).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return departs, count, nil
|
||||
}
|
||||
|
||||
func (r *departmentRepository) RebuildParentPath(ctx context.Context) error {
|
||||
query := `UPDATE sys_department AS tm
|
||||
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
||||
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
||||
FROM sys_department
|
||||
WHERE id = tm.id
|
||||
UNION ALL
|
||||
SELECT sys_department.id, sys_department.parent_id
|
||||
FROM sys_department,
|
||||
temp
|
||||
WHERE sys_department.id = temp.parent_id)
|
||||
SELECT id, parent_id
|
||||
FROM temp
|
||||
ORDER BY id) AS t)
|
||||
WHERE tm.status = 0;`
|
||||
return r.repo.DB(ctx).Exec(query).Error
|
||||
}
|
||||
195
internal/erpserver/repository/system/department/department.go
Normal file
195
internal/erpserver/repository/system/department/department.go
Normal file
@ -0,0 +1,195 @@
|
||||
package department
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.DepartmentRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Initialize(ctx context.Context) error {
|
||||
count, err := s.Count(ctx, dto.SearchDto{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
obj := system.Department{
|
||||
Name: "公司",
|
||||
ParentID: 0,
|
||||
ParentPath: ",0,",
|
||||
Status: 0,
|
||||
Sort: 6666,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
return s.Create(ctx, &obj)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.Department) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_department (
|
||||
name, parent_id, parent_path, status, sort
|
||||
) VALUES (
|
||||
:name, :parent_id, :parent_path, :status, :sort
|
||||
);`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Update(ctx context.Context, obj *system.Department) error {
|
||||
const q = `
|
||||
UPDATE sys_department
|
||||
SET name = :name,
|
||||
parent_id = :parent_id,
|
||||
parent_path = :parent_path,
|
||||
status = :status,
|
||||
sort = :sort,
|
||||
updated_at = :updated_at
|
||||
WHERE id = :id;`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Get(ctx context.Context, id int32) (*system.Department, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, parent_id, parent_path, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_department
|
||||
WHERE
|
||||
id = :id;`
|
||||
|
||||
data := map[string]any{
|
||||
"id": id,
|
||||
}
|
||||
|
||||
var depart system.Department
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &depart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select id department: %w", err)
|
||||
}
|
||||
|
||||
return &depart, nil
|
||||
}
|
||||
|
||||
func (s *store) All(ctx context.Context) ([]*system.Department, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, parent_id, parent_path, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_department;`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
var departs []system.Department
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), q, data, &departs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select all department: %w", err)
|
||||
}
|
||||
|
||||
return toPointer(departs), nil
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context, filter dto.SearchDto) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_department`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), buf.String(), data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count department: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
|
||||
func (s *store) List(ctx context.Context, filter dto.SearchDto) ([]*system.Department, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, parent_id, parent_path, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_department`
|
||||
|
||||
data := map[string]any{
|
||||
"offset": (filter.Page - 1) * filter.Rows,
|
||||
"rows_per_page": filter.Rows,
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
buf.WriteString(" ORDER BY id DESC")
|
||||
buf.WriteString(" LIMIT :rows_per_page OFFSET :offset")
|
||||
|
||||
var departs []system.Department
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), buf.String(), data, &departs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(departs), nil
|
||||
}
|
||||
|
||||
func (s *store) RebuildParentPath(ctx context.Context) error {
|
||||
query := `
|
||||
UPDATE sys_department AS tm
|
||||
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
||||
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
||||
FROM sys_department
|
||||
WHERE id = tm.id
|
||||
UNION ALL
|
||||
SELECT sys_department.id, sys_department.parent_id
|
||||
FROM sys_department,
|
||||
temp
|
||||
WHERE sys_department.id = temp.parent_id)
|
||||
SELECT id, parent_id
|
||||
FROM temp
|
||||
ORDER BY id) AS t)
|
||||
WHERE tm.status = 0;`
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), query, nil)
|
||||
}
|
||||
|
||||
func toPointer(data []system.Department) []*system.Department {
|
||||
var res []*system.Department
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
48
internal/erpserver/repository/system/department/filter.go
Normal file
48
internal/erpserver/repository/system/department/filter.go
Normal file
@ -0,0 +1,48 @@
|
||||
package department
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func applyFilter(filter dto.SearchDto, data map[string]any, buf *bytes.Buffer) {
|
||||
var wc []string
|
||||
|
||||
if filter.SearchTimeBegin != "" && filter.SearchTimeEnd == "" {
|
||||
data["start_at"] = filter.SearchTimeBegin
|
||||
data["end_at"] = filter.SearchTimeEnd
|
||||
wc = append(wc, "created_at BETWEEN :start_at AND :end_at")
|
||||
}
|
||||
|
||||
if filter.SearchEmail != "" {
|
||||
data["email"] = filter.SearchEmail
|
||||
wc = append(wc, "email LIKE :email")
|
||||
}
|
||||
|
||||
if filter.SearchID != 0 {
|
||||
data["id"] = filter.SearchID
|
||||
wc = append(wc, "id = :id")
|
||||
}
|
||||
|
||||
if filter.SearchParentID != 0 && filter.SearchParentID != 1 {
|
||||
data["parent_id"] = filter.SearchParentID
|
||||
wc = append(wc, "parent_id = :parent_id")
|
||||
}
|
||||
|
||||
if filter.SearchName != "" {
|
||||
data["name"] = filter.SearchName
|
||||
wc = append(wc, "name LIKE :name")
|
||||
}
|
||||
|
||||
if filter.SearchStatus != 9999 {
|
||||
data["status"] = filter.SearchStatus
|
||||
wc = append(wc, "status = :status")
|
||||
}
|
||||
|
||||
if len(wc) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteString(strings.Join(wc, " AND "))
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
)
|
||||
|
||||
type loginLogRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewLoginLogRepository(repo *repository.Repository) system.LoginLogRepository {
|
||||
return &loginLogRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *loginLogRepository) Create(ctx context.Context, obj *system.LoginLog) error {
|
||||
return s.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (s *loginLogRepository) GetLatest(ctx context.Context, email string) ([]*system.LoginLog, error) {
|
||||
var logs []*system.LoginLog
|
||||
err := s.repo.DB(ctx).
|
||||
Where("email = ?", email).
|
||||
Order("id DESC").
|
||||
Limit(2).
|
||||
Find(&logs).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (s *loginLogRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.LoginLog, int64, error) {
|
||||
query := s.repo.DB(ctx).
|
||||
Model(&system.LoginLog{}).
|
||||
Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd)
|
||||
if q.SearchEmail != "" {
|
||||
query = query.Where("email LIKE ?", "%"+q.SearchEmail+"%")
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := query.Count(&count).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var logs []*system.LoginLog
|
||||
err = query.
|
||||
Order("id DESC").
|
||||
Offset((q.Page - 1) * q.Rows).
|
||||
Limit(q.Rows).
|
||||
Find(&logs).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return logs, count, nil
|
||||
}
|
||||
|
||||
func (s *loginLogRepository) Count(ctx context.Context, email string) (int64, error) {
|
||||
var count int64
|
||||
err := s.repo.DB(ctx).
|
||||
Model(&system.LoginLog{}).
|
||||
Where("email = ?", email).
|
||||
Count(&count).
|
||||
Error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
28
internal/erpserver/repository/system/loginlog/filter.go
Normal file
28
internal/erpserver/repository/system/loginlog/filter.go
Normal file
@ -0,0 +1,28 @@
|
||||
package loginlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func applyFilter(filter dto.SearchDto, data map[string]any, buf *bytes.Buffer) {
|
||||
var wc []string
|
||||
|
||||
if filter.SearchTimeBegin != "" && filter.SearchTimeEnd == "" {
|
||||
data["start_at"] = filter.SearchTimeBegin
|
||||
data["end_at"] = filter.SearchTimeEnd
|
||||
wc = append(wc, "created_at BETWEEN :start_at AND :end_at")
|
||||
}
|
||||
|
||||
if filter.SearchEmail != "" {
|
||||
data["email"] = filter.SearchEmail
|
||||
wc = append(wc, "email LIKE :email")
|
||||
}
|
||||
|
||||
if len(wc) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteString(strings.Join(wc, " AND "))
|
||||
}
|
||||
}
|
||||
123
internal/erpserver/repository/system/loginlog/login_log.go
Normal file
123
internal/erpserver/repository/system/loginlog/login_log.go
Normal file
@ -0,0 +1,123 @@
|
||||
package loginlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.LoginLogRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.LoginLog) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_user_login_log (
|
||||
email, is_success, message, referer_url, url, os, ip, browser
|
||||
) VALUES (
|
||||
:email, :is_success, :message, :referer_url, :url, :os, :ip, :browser
|
||||
)`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) GetLatest(ctx context.Context, email string) ([]*system.LoginLog, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, created_at, email, is_success, message, referer_url, url, os, ip, browser
|
||||
FROM
|
||||
sys_user_login_log
|
||||
WHERE
|
||||
email = :email
|
||||
ORDER BY
|
||||
id DESC
|
||||
LIMIT 2;`
|
||||
|
||||
data := map[string]any{
|
||||
"email": email,
|
||||
}
|
||||
|
||||
var logs []system.LoginLog
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), q, data, &logs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(logs), nil
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context, filter dto.SearchDto) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_user_login_log`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), buf.String(), data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count login log: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
|
||||
func (s *store) List(ctx context.Context, filter dto.SearchDto) ([]*system.LoginLog, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, created_at, email, is_success, message, referer_url, url, os, ip, browser
|
||||
FROM
|
||||
sys_user_login_log`
|
||||
|
||||
data := map[string]any{
|
||||
"offset": (filter.Page - 1) * filter.Rows,
|
||||
"rows_per_page": filter.Rows,
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
buf.WriteString(" ORDER BY id DESC")
|
||||
buf.WriteString(" LIMIT :rows_per_page OFFSET :offset")
|
||||
|
||||
var logs []system.LoginLog
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), buf.String(), data, &logs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(logs), nil
|
||||
}
|
||||
|
||||
func toPointer(data []system.LoginLog) []*system.LoginLog {
|
||||
var res []*system.LoginLog
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -1,83 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/database"
|
||||
)
|
||||
|
||||
type menuRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewMenuRepository(repo *repository.Repository) system.MenuRepository {
|
||||
return &menuRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *menuRepository) Create(ctx context.Context, obj *system.Menu) (*system.Menu, error) {
|
||||
err := r.repo.DB(ctx).Create(obj).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (r *menuRepository) Update(ctx context.Context, obj *system.Menu) error {
|
||||
return r.repo.DB(ctx).Save(obj).Error
|
||||
}
|
||||
|
||||
func (r *menuRepository) Get(ctx context.Context, id int32) (*system.Menu, error) {
|
||||
var menu system.Menu
|
||||
err := r.repo.DB(ctx).Where("id = ?", id).First(&menu).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("menu %d not found: %w", id, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &menu, nil
|
||||
}
|
||||
|
||||
func (r *menuRepository) GetByUrl(ctx context.Context, url string) (*system.Menu, error) {
|
||||
var menu system.Menu
|
||||
err := r.repo.DB(ctx).Where("url = ?", url).First(&menu).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("menu by url %s not found: %w", url, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &menu, nil
|
||||
}
|
||||
|
||||
func (r *menuRepository) All(ctx context.Context) ([]*system.Menu, error) {
|
||||
var menus []*system.Menu
|
||||
err := r.repo.DB(ctx).Find(&menus).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return menus, nil
|
||||
}
|
||||
|
||||
func (r *menuRepository) RebuildParentPath(ctx context.Context) error {
|
||||
query := `UPDATE sys_menu AS tm
|
||||
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
||||
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
||||
FROM sys_menu
|
||||
WHERE id = tm.id
|
||||
UNION ALL
|
||||
SELECT sys_menu.id, sys_menu.parent_id
|
||||
FROM sys_menu,
|
||||
temp
|
||||
WHERE sys_menu.id = temp.parent_id)
|
||||
SELECT id, parent_id
|
||||
FROM temp
|
||||
ORDER BY id) AS t)
|
||||
WHERE tm.status = 0;`
|
||||
return r.repo.DB(ctx).Exec(query).Error
|
||||
}
|
||||
179
internal/erpserver/repository/system/menu/menu.go
Normal file
179
internal/erpserver/repository/system/menu/menu.go
Normal file
@ -0,0 +1,179 @@
|
||||
package menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.MenuRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.Menu) (*system.Menu, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_menu (
|
||||
name, display_name, url, type, parent_id, parent_path, avatar, style, visible, is_list, status, sort
|
||||
) VALUES (
|
||||
:name, :display_name, :url, :type, :parent_id, :parent_path, :avatar, :style, :visible, :is_list, :status, :sort
|
||||
);`
|
||||
|
||||
err := sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (s *store) Update(ctx context.Context, obj *system.Menu) error {
|
||||
const q = `
|
||||
UPDATE sys_menu
|
||||
SET name = :name,
|
||||
display_name = :display_name,
|
||||
url = :url,
|
||||
type = :type,
|
||||
parent_id = :parent_id,
|
||||
parent_path = :parent_path,
|
||||
avatar = :avatar,
|
||||
style = :style,
|
||||
visible = :visible,
|
||||
is_list = :is_list,
|
||||
status = :status,
|
||||
sort = :sort,
|
||||
updated_at = :updated_at
|
||||
WHERE id = :id;`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Get(ctx context.Context, id int32) (*system.Menu, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, url, type, parent_id, parent_path, avatar, style,
|
||||
visible, is_list, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_menu
|
||||
WHERE
|
||||
id = :id;`
|
||||
|
||||
data := map[string]any{
|
||||
"id": id,
|
||||
}
|
||||
|
||||
var menu system.Menu
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &menu)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select id menu: %w", err)
|
||||
}
|
||||
|
||||
return &menu, nil
|
||||
}
|
||||
|
||||
func (s *store) GetByUrl(ctx context.Context, url string) (*system.Menu, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, url, type, parent_id, parent_path, avatar, style,
|
||||
visible, is_list, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_menu
|
||||
WHERE
|
||||
url = :url;`
|
||||
|
||||
data := map[string]any{
|
||||
"url": url,
|
||||
}
|
||||
|
||||
var menu system.Menu
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &menu)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select id menu: %w", err)
|
||||
}
|
||||
|
||||
return &menu, nil
|
||||
}
|
||||
|
||||
func (s *store) All(ctx context.Context) ([]*system.Menu, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, url, type, parent_id, parent_path, avatar, style,
|
||||
visible, is_list, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_menu;`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
var menus []system.Menu
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), q, data, &menus)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select all menu: %w", err)
|
||||
}
|
||||
|
||||
return toPointer(menus), nil
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_menu`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count menu: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
|
||||
func (s *store) RebuildParentPath(ctx context.Context) error {
|
||||
query := `
|
||||
UPDATE sys_menu AS tm
|
||||
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
||||
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
||||
FROM sys_menu
|
||||
WHERE id = tm.id
|
||||
UNION ALL
|
||||
SELECT sys_menu.id, sys_menu.parent_id
|
||||
FROM sys_menu,
|
||||
temp
|
||||
WHERE sys_menu.id = temp.parent_id)
|
||||
SELECT id, parent_id
|
||||
FROM temp
|
||||
ORDER BY id) AS t)
|
||||
WHERE tm.status = 0;`
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), query, nil)
|
||||
}
|
||||
|
||||
func toPointer(data []system.Menu) []*system.Menu {
|
||||
var res []*system.Menu
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package system
|
||||
package menu
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -10,16 +10,17 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
var count int64
|
||||
if err := r.repo.DB(ctx).Model(&system.Menu{}).Count(&count).Error; err != nil {
|
||||
func (s *store) Initialize(ctx context.Context) error {
|
||||
count, err := s.Count(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sys, err := r.Create(ctx, &system.Menu{
|
||||
sys, err := s.Create(ctx, &system.Menu{
|
||||
Name: "系统管理",
|
||||
DisplayName: "系统管理",
|
||||
Url: uuid.Must(uuid.NewRandom()).String(),
|
||||
@ -39,7 +40,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
accountPermission, err := r.Create(ctx, &system.Menu{
|
||||
accountPermission, err := s.Create(ctx, &system.Menu{
|
||||
Name: "账户权限",
|
||||
DisplayName: "账户权限",
|
||||
Url: uuid.Must(uuid.NewRandom()).String(),
|
||||
@ -59,7 +60,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
systemMenu, err := r.Create(ctx, &system.Menu{
|
||||
systemMenu, err := s.Create(ctx, &system.Menu{
|
||||
Name: "菜单管理",
|
||||
DisplayName: "菜单管理",
|
||||
Url: "/system/menu/list",
|
||||
@ -79,7 +80,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
systemRole, err := r.Create(ctx, &system.Menu{
|
||||
systemRole, err := s.Create(ctx, &system.Menu{
|
||||
Name: "角色管理",
|
||||
DisplayName: "角色管理",
|
||||
Url: "/system/role/list",
|
||||
@ -99,7 +100,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
systemDepartment, err := r.Create(ctx, &system.Menu{
|
||||
systemDepartment, err := s.Create(ctx, &system.Menu{
|
||||
Name: "部门管理",
|
||||
DisplayName: "部门管理",
|
||||
Url: "/system/department/list",
|
||||
@ -119,7 +120,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
systemUser, err := r.Create(ctx, &system.Menu{
|
||||
systemUser, err := s.Create(ctx, &system.Menu{
|
||||
Name: "用户管理",
|
||||
DisplayName: "用户管理",
|
||||
Url: "/system/user/list",
|
||||
@ -139,7 +140,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "登陆日志",
|
||||
DisplayName: "登陆日志",
|
||||
Url: "/system/login_log/list",
|
||||
@ -159,7 +160,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "审计日志",
|
||||
DisplayName: "审计日志",
|
||||
Url: "/system/audit_log/list",
|
||||
@ -180,7 +181,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 菜单
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增",
|
||||
DisplayName: "新增",
|
||||
Url: "/system/menu/add",
|
||||
@ -200,7 +201,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增子菜单",
|
||||
DisplayName: "新增子菜单",
|
||||
Url: "/system/menu/add_children",
|
||||
@ -220,7 +221,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "编辑",
|
||||
DisplayName: "编辑",
|
||||
Url: "/system/menu/edit",
|
||||
@ -240,7 +241,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "保存",
|
||||
DisplayName: "保存",
|
||||
Url: "/system/menu/save",
|
||||
@ -260,7 +261,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "数据",
|
||||
DisplayName: "数据",
|
||||
Url: "/system/menu/data",
|
||||
@ -280,7 +281,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "刷新",
|
||||
DisplayName: "刷新",
|
||||
Url: "/system/menu/refresh_cache",
|
||||
@ -301,7 +302,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 角色
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增",
|
||||
DisplayName: "新增",
|
||||
Url: "/system/role/add",
|
||||
@ -321,7 +322,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "编辑",
|
||||
DisplayName: "编辑",
|
||||
Url: "/system/role/edit",
|
||||
@ -341,7 +342,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "保存",
|
||||
DisplayName: "保存",
|
||||
Url: "/system/role/save",
|
||||
@ -361,7 +362,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "数据",
|
||||
DisplayName: "数据",
|
||||
Url: "/system/role/data",
|
||||
@ -381,7 +382,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "刷新",
|
||||
DisplayName: "刷新",
|
||||
Url: "/system/role/refresh_cache",
|
||||
@ -401,7 +402,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "重建父路径",
|
||||
DisplayName: "重建父路径",
|
||||
Url: "/system/role/rebuild_parent_path",
|
||||
@ -421,7 +422,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "权限刷新",
|
||||
DisplayName: "权限刷新",
|
||||
Url: "/system/role/refresh_role_menus",
|
||||
@ -441,7 +442,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "设置权限",
|
||||
DisplayName: "设置权限",
|
||||
Url: "/system/role/set_menu",
|
||||
@ -462,7 +463,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 部门
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增",
|
||||
DisplayName: "新增",
|
||||
Url: "/system/department/add",
|
||||
@ -482,7 +483,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增子部门",
|
||||
DisplayName: "新增子部门",
|
||||
Url: "/system/department/add_children",
|
||||
@ -502,7 +503,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "编辑",
|
||||
DisplayName: "编辑",
|
||||
Url: "/system/department/edit",
|
||||
@ -522,7 +523,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "保存",
|
||||
DisplayName: "保存",
|
||||
Url: "/system/department/save",
|
||||
@ -542,7 +543,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "数据",
|
||||
DisplayName: "数据",
|
||||
Url: "/system/department/data",
|
||||
@ -562,7 +563,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "刷新",
|
||||
DisplayName: "刷新",
|
||||
Url: "/system/department/refresh_cache",
|
||||
@ -582,7 +583,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "重建父路径",
|
||||
DisplayName: "重建父路径",
|
||||
Url: "/system/department/rebuild_parent_path",
|
||||
@ -603,7 +604,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 用户
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增",
|
||||
DisplayName: "新增",
|
||||
Url: "/system/user/add",
|
||||
@ -623,7 +624,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "编辑",
|
||||
DisplayName: "编辑",
|
||||
Url: "/system/user/edit",
|
||||
@ -643,7 +644,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "基本资料",
|
||||
DisplayName: "基本资料",
|
||||
Url: "/system/user/profile",
|
||||
@ -663,7 +664,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "保存",
|
||||
DisplayName: "保存",
|
||||
Url: "/system/user/save",
|
||||
@ -684,7 +685,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 基础数据
|
||||
basicData, err := r.Create(ctx, &system.Menu{
|
||||
basicData, err := s.Create(ctx, &system.Menu{
|
||||
Name: "基础数据",
|
||||
DisplayName: "基础数据",
|
||||
Url: uuid.Must(uuid.NewRandom()).String(),
|
||||
@ -705,7 +706,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 系统配置
|
||||
systemConfig, err := r.Create(ctx, &system.Menu{
|
||||
systemConfig, err := s.Create(ctx, &system.Menu{
|
||||
Name: "系统属性",
|
||||
DisplayName: "系统属性",
|
||||
Url: "/system/config/list",
|
||||
@ -725,7 +726,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "新增",
|
||||
DisplayName: "新增",
|
||||
Url: "/system/config/add",
|
||||
@ -745,7 +746,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "编辑",
|
||||
DisplayName: "编辑",
|
||||
Url: "/system/config/edit",
|
||||
@ -765,7 +766,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "保存",
|
||||
DisplayName: "保存",
|
||||
Url: "/system/config/save",
|
||||
@ -785,7 +786,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "重置Pear",
|
||||
DisplayName: "重置Pear",
|
||||
Url: "/system/config/reset_pear",
|
||||
@ -805,7 +806,7 @@ func (r *menuRepository) Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = r.Create(ctx, &system.Menu{
|
||||
_, err = s.Create(ctx, &system.Menu{
|
||||
Name: "刷新",
|
||||
DisplayName: "刷新",
|
||||
Url: "/system/config/refresh_cache",
|
||||
@ -1,148 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/database"
|
||||
)
|
||||
|
||||
type roleRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewRoleRepository(repo *repository.Repository) system.RoleRepository {
|
||||
return &roleRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roleRepository) Initialize(ctx context.Context) (*system.Role, error) {
|
||||
var count int64
|
||||
if err := r.repo.DB(ctx).Model(&system.Role{}).Count(&count).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
obj := system.Role{
|
||||
Name: "Company",
|
||||
DisplayName: "公司",
|
||||
Vip: false,
|
||||
ParentID: 0,
|
||||
ParentPath: ",0,",
|
||||
Status: 0,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := r.Create(ctx, &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj1 := system.Role{
|
||||
Name: "SuperAdmin",
|
||||
DisplayName: "超级管理员",
|
||||
Vip: true,
|
||||
ParentID: obj.ID,
|
||||
ParentPath: fmt.Sprintf(",0,%d,", obj.ID),
|
||||
Status: 0,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := r.Create(ctx, &obj1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &obj1, nil
|
||||
}
|
||||
|
||||
var role system.Role
|
||||
err := r.repo.DB(ctx).Where("vip = ?", true).First(&role).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (r *roleRepository) Create(ctx context.Context, obj *system.Role) error {
|
||||
return r.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (r *roleRepository) Update(ctx context.Context, obj *system.Role) error {
|
||||
return r.repo.DB(ctx).Save(obj).Error
|
||||
}
|
||||
|
||||
func (r *roleRepository) Get(ctx context.Context, id int32) (*system.Role, error) {
|
||||
var role system.Role
|
||||
err := r.repo.DB(ctx).Where("id = ?", id).First(&role).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("role %d not found: %w", id, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (r *roleRepository) All(ctx context.Context) ([]*system.Role, error) {
|
||||
var roles []*system.Role
|
||||
err := r.repo.DB(ctx).Find(&roles).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
func (r *roleRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.Role, int64, error) {
|
||||
query := r.repo.DB(ctx).
|
||||
Model(&system.Role{}).
|
||||
Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd)
|
||||
if q.SearchID != 0 {
|
||||
query = query.Where("id = ?", q.SearchID)
|
||||
}
|
||||
if q.SearchParentID != 0 && q.SearchParentID != 1 {
|
||||
query = query.Where("parent_id = ?", q.SearchParentID)
|
||||
}
|
||||
if q.SearchName != "" {
|
||||
query = query.Where("name LIKE ?", "%"+q.SearchName+"%")
|
||||
}
|
||||
if q.SearchStatus != 9999 {
|
||||
query = query.Where("status = ?", q.SearchStatus)
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := query.Count(&count).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var departs []*system.Role
|
||||
err = query.
|
||||
Order("id DESC").
|
||||
Offset((q.Page - 1) * q.Rows).
|
||||
Limit(q.Rows).
|
||||
Find(&departs).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return departs, count, nil
|
||||
}
|
||||
|
||||
func (r *roleRepository) RebuildParentPath(ctx context.Context) error {
|
||||
query := `UPDATE sys_role AS tm
|
||||
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
||||
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
||||
FROM sys_role
|
||||
WHERE id = tm.id
|
||||
UNION ALL
|
||||
SELECT sys_role.id, sys_role.parent_id
|
||||
FROM sys_role,
|
||||
temp
|
||||
WHERE sys_role.id = temp.parent_id)
|
||||
SELECT id, parent_id
|
||||
FROM temp
|
||||
ORDER BY id) AS t)
|
||||
WHERE tm.status = 0;`
|
||||
return r.repo.DB(ctx).Exec(query).Error
|
||||
}
|
||||
48
internal/erpserver/repository/system/role/filter.go
Normal file
48
internal/erpserver/repository/system/role/filter.go
Normal file
@ -0,0 +1,48 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func applyFilter(filter dto.SearchDto, data map[string]any, buf *bytes.Buffer) {
|
||||
var wc []string
|
||||
|
||||
if filter.SearchTimeBegin != "" && filter.SearchTimeEnd == "" {
|
||||
data["start_at"] = filter.SearchTimeBegin
|
||||
data["end_at"] = filter.SearchTimeEnd
|
||||
wc = append(wc, "created_at BETWEEN :start_at AND :end_at")
|
||||
}
|
||||
|
||||
if filter.SearchEmail != "" {
|
||||
data["email"] = filter.SearchEmail
|
||||
wc = append(wc, "email LIKE :email")
|
||||
}
|
||||
|
||||
if filter.SearchID != 0 {
|
||||
data["id"] = filter.SearchID
|
||||
wc = append(wc, "id = :id")
|
||||
}
|
||||
|
||||
if filter.SearchParentID != 0 && filter.SearchParentID != 1 {
|
||||
data["parent_id"] = filter.SearchParentID
|
||||
wc = append(wc, "parent_id = :parent_id")
|
||||
}
|
||||
|
||||
if filter.SearchName != "" {
|
||||
data["name"] = filter.SearchName
|
||||
wc = append(wc, "name LIKE :name")
|
||||
}
|
||||
|
||||
if filter.SearchStatus != 9999 {
|
||||
data["status"] = filter.SearchStatus
|
||||
wc = append(wc, "status = :status")
|
||||
}
|
||||
|
||||
if len(wc) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteString(strings.Join(wc, " AND "))
|
||||
}
|
||||
}
|
||||
236
internal/erpserver/repository/system/role/role.go
Normal file
236
internal/erpserver/repository/system/role/role.go
Normal file
@ -0,0 +1,236 @@
|
||||
package role
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.RoleRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Initialize(ctx context.Context) (*system.Role, error) {
|
||||
count, err := s.Count(ctx, dto.SearchDto{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
obj := system.Role{
|
||||
Name: "Company",
|
||||
DisplayName: "公司",
|
||||
Vip: false,
|
||||
ParentID: 0,
|
||||
ParentPath: ",0,",
|
||||
Status: 0,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := s.Create(ctx, &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj1 := system.Role{
|
||||
Name: "SuperAdmin",
|
||||
DisplayName: "超级管理员",
|
||||
Vip: true,
|
||||
ParentID: obj.ID,
|
||||
ParentPath: fmt.Sprintf(",0,%d,", obj.ID),
|
||||
Status: 0,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
if err := s.Create(ctx, &obj1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &obj1, nil
|
||||
}
|
||||
|
||||
return s.GetByVip(ctx, true)
|
||||
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.Role) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_role (
|
||||
name, display_name, parent_id, parent_path, vip, status, sort
|
||||
) VALUES (
|
||||
:name, :display_name, :parent_id, :parent_path, :vip, :status, :sort
|
||||
)`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Update(ctx context.Context, obj *system.Role) error {
|
||||
const q = `
|
||||
UPDATE sys_role
|
||||
SET name = :name,
|
||||
display_name = :display_name,
|
||||
parent_id = :parent_id,
|
||||
parent_path = :parent_path,
|
||||
status = :status,
|
||||
sort = :sort,
|
||||
updated_at = :updated_at
|
||||
WHERE id = :id;`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) GetByVip(ctx context.Context, vip bool) (*system.Role, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, parent_id, parent_path, vip, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_role
|
||||
WHERE
|
||||
vip = :vip;`
|
||||
|
||||
data := map[string]any{
|
||||
"vip": vip,
|
||||
}
|
||||
|
||||
var role system.Role
|
||||
if err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &role); err != nil {
|
||||
return nil, fmt.Errorf("select vip role: %w", err)
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (s *store) Get(ctx context.Context, id int32) (*system.Role, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, parent_id, parent_path, vip, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_role
|
||||
WHERE
|
||||
id = :id;`
|
||||
|
||||
data := map[string]any{
|
||||
"id": id,
|
||||
}
|
||||
|
||||
var role system.Role
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &role)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select id role: %w", err)
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
func (s *store) All(ctx context.Context) ([]*system.Role, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, parent_id, parent_path, vip, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_role;`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
var roles []system.Role
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), q, data, &roles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select all role: %w", err)
|
||||
}
|
||||
|
||||
return toPointer(roles), nil
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context, filter dto.SearchDto) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_role`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), buf.String(), data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count role: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
|
||||
func (s *store) List(ctx context.Context, filter dto.SearchDto) ([]*system.Role, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, name, display_name, parent_id, parent_path, vip, status, sort, created_at, updated_at
|
||||
FROM
|
||||
sys_role`
|
||||
|
||||
data := map[string]any{
|
||||
"offset": (filter.Page - 1) * filter.Rows,
|
||||
"rows_per_page": filter.Rows,
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
buf.WriteString(" ORDER BY id DESC")
|
||||
buf.WriteString(" LIMIT :rows_per_page OFFSET :offset")
|
||||
|
||||
var roles []system.Role
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), buf.String(), data, &roles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(roles), nil
|
||||
}
|
||||
|
||||
func (s *store) RebuildParentPath(ctx context.Context) error {
|
||||
query := `UPDATE sys_role AS tm
|
||||
SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') || ','
|
||||
FROM (WITH RECURSIVE temp (id, parent_id) AS (SELECT id, tm.parent_id
|
||||
FROM sys_role
|
||||
WHERE id = tm.id
|
||||
UNION ALL
|
||||
SELECT sys_role.id, sys_role.parent_id
|
||||
FROM sys_role,
|
||||
temp
|
||||
WHERE sys_role.id = temp.parent_id)
|
||||
SELECT id, parent_id
|
||||
FROM temp
|
||||
ORDER BY id) AS t)
|
||||
WHERE tm.status = 0;`
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), query, nil)
|
||||
}
|
||||
|
||||
func toPointer(data []system.Role) []*system.Role {
|
||||
var res []*system.Role
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
)
|
||||
|
||||
type roleMenuRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewRoleMenuRepository(repo *repository.Repository) system.RoleMenuRepository {
|
||||
return &roleMenuRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roleMenuRepository) Create(ctx context.Context, obj []*system.RoleMenu) error {
|
||||
return r.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (r *roleMenuRepository) DeleteByRoleID(ctx context.Context, roleID int32) error {
|
||||
return r.repo.DB(ctx).Where("role_id = ?", roleID).Delete(&system.RoleMenu{}).Error
|
||||
}
|
||||
|
||||
func (r *roleMenuRepository) ListByRoleID(ctx context.Context, roleID int32) ([]*system.RoleMenu, error) {
|
||||
var roleMenus []*system.RoleMenu
|
||||
err := r.repo.DB(ctx).Where("role_id = ?", roleID).Find(&roleMenus).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return roleMenus, nil
|
||||
}
|
||||
86
internal/erpserver/repository/system/rolemenu/role_menu.go
Normal file
86
internal/erpserver/repository/system/rolemenu/role_menu.go
Normal file
@ -0,0 +1,86 @@
|
||||
package rolemenu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.RoleMenuRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj []*system.RoleMenu) error {
|
||||
if len(obj) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_role_menu (
|
||||
role_id, menu_id
|
||||
) VALUES (
|
||||
:role_id, :menu_id
|
||||
);`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) DeleteByRoleID(ctx context.Context, roleID int32) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
DELETE FROM
|
||||
sys_role_menu
|
||||
WHERE
|
||||
role_id = :role_id;`
|
||||
|
||||
data := map[string]any{
|
||||
"role_id": roleID,
|
||||
}
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, data)
|
||||
}
|
||||
|
||||
func (s *store) ListByRoleID(ctx context.Context, roleID int32) ([]*system.RoleMenu, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
role_id, menu_id
|
||||
FROM
|
||||
sys_role_menu
|
||||
WHERE
|
||||
role_id = :role_id;`
|
||||
|
||||
data := map[string]any{
|
||||
"role_id": roleID,
|
||||
}
|
||||
|
||||
var roleMenus []system.RoleMenu
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), q, data, &roleMenus)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select role menu by role id: %w", err)
|
||||
}
|
||||
|
||||
return toPointer(roleMenus), nil
|
||||
}
|
||||
|
||||
func toPointer(data []system.RoleMenu) []*system.RoleMenu {
|
||||
var res []*system.RoleMenu
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/crypto"
|
||||
"management/internal/pkg/database"
|
||||
"management/internal/pkg/rand"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type userRepository struct {
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewUserRepository(repo *repository.Repository) system.UserRepository {
|
||||
return &userRepository{
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *userRepository) Initialize(ctx context.Context, departId, roleId int32) error {
|
||||
var count int64
|
||||
if err := s.repo.DB(ctx).Model(&system.User{}).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
salt, err := rand.String(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password := "secret"
|
||||
hashedPassword, err := crypto.BcryptHashPassword(password + salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
initTime, err := time.ParseInLocation(time.DateTime, "0001-01-01 00:00:00", time.Local)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := system.User{
|
||||
Uuid: uuid.Must(uuid.NewV7()),
|
||||
Email: "1185230223@qq.com",
|
||||
Username: "kenneth",
|
||||
HashedPassword: hashedPassword,
|
||||
Salt: salt,
|
||||
Avatar: "/assets/admin/images/avatar.jpg",
|
||||
Gender: 1,
|
||||
DepartmentID: departId,
|
||||
RoleID: roleId,
|
||||
Status: 0,
|
||||
ChangePasswordAt: initTime,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
return s.Create(ctx, &user)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *userRepository) Create(ctx context.Context, obj *system.User) error {
|
||||
return s.repo.DB(ctx).Create(obj).Error
|
||||
}
|
||||
|
||||
func (s *userRepository) Update(ctx context.Context, obj *system.User) error {
|
||||
return s.repo.DB(ctx).Save(obj).Error
|
||||
}
|
||||
|
||||
func (s *userRepository) Get(ctx context.Context, id int32) (*system.User, error) {
|
||||
var user system.User
|
||||
err := s.repo.DB(ctx).Where("id = ?", id).First(&user).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("user %d not found: %w", id, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *userRepository) GetByEmail(ctx context.Context, email string) (*system.User, error) {
|
||||
var user system.User
|
||||
err := s.repo.DB(ctx).Where("email = ?", email).First(&user).Error
|
||||
if err != nil {
|
||||
if database.IsNoRows(err) {
|
||||
return nil, fmt.Errorf("user by email %s not found: %w", email, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *userRepository) All(ctx context.Context) ([]*system.User, error) {
|
||||
var users []*system.User
|
||||
err := s.repo.DB(ctx).Find(&users).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (s *userRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.User, int64, error) {
|
||||
query := s.repo.DB(ctx).
|
||||
Model(&system.User{}).
|
||||
Preload("Role").
|
||||
Preload("Department").
|
||||
Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd)
|
||||
if q.SearchID != 0 {
|
||||
query = query.Where("id = ?", q.SearchID)
|
||||
}
|
||||
if q.SearchName != "" {
|
||||
query = query.Where("username LIKE ?", "%"+q.SearchName+"%")
|
||||
}
|
||||
if q.SearchEmail != "" {
|
||||
query = query.Where("email LIKE ?", "%"+q.SearchEmail+"%")
|
||||
}
|
||||
if q.SearchStatus != 9999 {
|
||||
query = query.Where("status = ?", q.SearchStatus)
|
||||
}
|
||||
|
||||
var count int64
|
||||
err := query.Count(&count).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var users []*system.User
|
||||
err = query.
|
||||
Order("id DESC").
|
||||
Offset((q.Page - 1) * q.Rows).
|
||||
Limit(q.Rows).
|
||||
Find(&users).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return users, count, nil
|
||||
}
|
||||
43
internal/erpserver/repository/system/user/filter.go
Normal file
43
internal/erpserver/repository/system/user/filter.go
Normal file
@ -0,0 +1,43 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
)
|
||||
|
||||
func applyFilter(filter dto.SearchDto, data map[string]any, buf *bytes.Buffer) {
|
||||
var wc []string
|
||||
|
||||
if filter.SearchTimeBegin != "" && filter.SearchTimeEnd == "" {
|
||||
data["start_at"] = filter.SearchTimeBegin
|
||||
data["end_at"] = filter.SearchTimeEnd
|
||||
wc = append(wc, "created_at BETWEEN :start_at AND :end_at")
|
||||
}
|
||||
|
||||
if filter.SearchID != 0 {
|
||||
data["id"] = filter.SearchID
|
||||
wc = append(wc, "id = :id")
|
||||
}
|
||||
|
||||
if filter.SearchName != "" {
|
||||
data["username"] = filter.SearchName
|
||||
wc = append(wc, "username LIKE :username")
|
||||
}
|
||||
|
||||
if filter.SearchEmail != "" {
|
||||
data["email"] = filter.SearchEmail
|
||||
wc = append(wc, "email LIKE :email")
|
||||
}
|
||||
|
||||
if filter.SearchStatus != 9999 {
|
||||
data["status"] = filter.SearchStatus
|
||||
wc = append(wc, "status = :status")
|
||||
}
|
||||
|
||||
if len(wc) > 0 {
|
||||
buf.WriteString(" WHERE ")
|
||||
buf.WriteString(strings.Join(wc, " AND "))
|
||||
}
|
||||
}
|
||||
234
internal/erpserver/repository/system/user/user.go
Normal file
234
internal/erpserver/repository/system/user/user.go
Normal file
@ -0,0 +1,234 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/crypto"
|
||||
"management/internal/pkg/rand"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
db *repository.Store
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewStore(db *repository.Store, log *logger.Logger) system.UserRepository {
|
||||
return &store{
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) Initialize(ctx context.Context, departId, roleId int32) error {
|
||||
count, err := s.Count(ctx, dto.SearchDto{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
salt, err := rand.String(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password := "secret"
|
||||
hashedPassword, err := crypto.BcryptHashPassword(password + salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
initTime, err := time.ParseInLocation(time.DateTime, "0001-01-01 00:00:00", time.Local)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := system.User{
|
||||
Uuid: uuid.Must(uuid.NewV7()),
|
||||
Email: "1185230223@qq.com",
|
||||
Username: "kenneth",
|
||||
HashedPassword: hashedPassword,
|
||||
Salt: salt,
|
||||
Avatar: "/assets/admin/images/avatar.jpg",
|
||||
Gender: 1,
|
||||
DepartmentID: departId,
|
||||
RoleID: roleId,
|
||||
Status: 0,
|
||||
ChangePasswordAt: initTime,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
return s.Create(ctx, &user)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, obj *system.User) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
INSERT INTO sys_user (
|
||||
uuid, email, username, hashed_password, salt, avatar, gender, department_id, role_id, status
|
||||
) VALUES (
|
||||
:uuid, :email, :username, :hashed_password, :salt, :avatar, :gender, :department_id, :role_id, :status
|
||||
);`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Update(ctx context.Context, obj *system.User) error {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
UPDATE sys_user
|
||||
SET email = :email,
|
||||
username = :username,
|
||||
hashed_password = :hashed_password,
|
||||
avatar = :avatar,
|
||||
gender = :gender,
|
||||
department_id = :department_id,
|
||||
role_id = :role_id,
|
||||
status = :status,
|
||||
change_password_at = :change_password_at,
|
||||
updated_at = :updated_at
|
||||
WHERE id = :id;`
|
||||
|
||||
return sqldb.NamedExecContext(ctx, s.log, s.db.DB(ctx), q, obj)
|
||||
}
|
||||
|
||||
func (s *store) Get(ctx context.Context, id int32) (*system.User, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, uuid, email, username, hashed_password, salt, avatar, gender, department_id,
|
||||
role_id, status, change_password_at, created_at, updated_at
|
||||
FROM
|
||||
sys_user
|
||||
WHERE
|
||||
id = :id;`
|
||||
|
||||
data := map[string]any{
|
||||
"id": id,
|
||||
}
|
||||
|
||||
var user system.User
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select id user: %w", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *store) GetByEmail(ctx context.Context, email string) (*system.User, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, uuid, email, username, hashed_password, salt, avatar, gender, department_id,
|
||||
role_id, status, change_password_at, created_at, updated_at
|
||||
FROM
|
||||
sys_user
|
||||
WHERE
|
||||
email = :email;`
|
||||
|
||||
data := map[string]any{
|
||||
"email": email,
|
||||
}
|
||||
|
||||
var user system.User
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), q, data, &user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select email user: %w", err)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (s *store) All(ctx context.Context) ([]*system.User, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, uuid, email, username, hashed_password, salt, avatar, gender, department_id,
|
||||
role_id, status, change_password_at, created_at, updated_at
|
||||
FROM
|
||||
sys_user;`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
var users []system.User
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), q, data, &users)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("select all role: %w", err)
|
||||
}
|
||||
|
||||
return toPointer(users), nil
|
||||
}
|
||||
|
||||
func (s *store) Count(ctx context.Context, filter dto.SearchDto) (int64, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
sys_user`
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
var count struct {
|
||||
Count int64 `db:"count"`
|
||||
}
|
||||
|
||||
err := sqldb.NamedQueryStruct(ctx, s.log, s.db.DB(ctx), buf.String(), data, &count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("select count user: %w", err)
|
||||
}
|
||||
|
||||
return count.Count, nil
|
||||
}
|
||||
|
||||
func (s *store) List(ctx context.Context, filter dto.SearchDto) ([]*system.User, error) {
|
||||
//goland:noinspection ALL
|
||||
const q = `
|
||||
SELECT
|
||||
id, uuid, email, username, hashed_password, salt, avatar, gender, department_id,
|
||||
role_id, status, change_password_at, created_at, updated_at
|
||||
FROM
|
||||
sys_user`
|
||||
|
||||
data := map[string]any{
|
||||
"offset": (filter.Page - 1) * filter.Rows,
|
||||
"rows_per_page": filter.Rows,
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString(q)
|
||||
applyFilter(filter, data, buf)
|
||||
|
||||
buf.WriteString(" ORDER BY id DESC")
|
||||
buf.WriteString(" LIMIT :rows_per_page OFFSET :offset")
|
||||
|
||||
var users []system.User
|
||||
err := sqldb.NamedQuerySlice(ctx, s.log, s.db.DB(ctx), buf.String(), data, &users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return toPointer(users), nil
|
||||
}
|
||||
|
||||
func toPointer(data []system.User) []*system.User {
|
||||
var res []*system.User
|
||||
for _, v := range data {
|
||||
res = append(res, &v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"management/internal/erpserver/model/form"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/model/view"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/pkg/cache"
|
||||
"management/internal/pkg/session"
|
||||
|
||||
@ -16,20 +15,17 @@ import (
|
||||
|
||||
type Service struct {
|
||||
Log *logger.Logger
|
||||
Tx repository.Transaction
|
||||
Session session.Manager
|
||||
Redis cache.Cache
|
||||
}
|
||||
|
||||
func NewService(
|
||||
log *logger.Logger,
|
||||
tx repository.Transaction,
|
||||
session session.Manager,
|
||||
redis cache.Cache,
|
||||
) *Service {
|
||||
return &Service{
|
||||
Log: log,
|
||||
Tx: tx,
|
||||
Session: session,
|
||||
Redis: redis,
|
||||
}
|
||||
|
||||
@ -29,5 +29,15 @@ func (b *auditLogService) BatchCreate(ctx context.Context, objs []*system.AuditL
|
||||
}
|
||||
|
||||
func (b *auditLogService) List(ctx context.Context, q dto.SearchDto) ([]*system.AuditLog, int64, error) {
|
||||
return b.repo.List(ctx, q)
|
||||
count, err := b.repo.Count(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, err := b.repo.List(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return res, count, nil
|
||||
}
|
||||
|
||||
@ -41,7 +41,17 @@ func (s *configService) Get(ctx context.Context, id int32) (*system.Config, erro
|
||||
}
|
||||
|
||||
func (s *configService) List(ctx context.Context, q dto.SearchDto) ([]*system.Config, int64, error) {
|
||||
return s.repo.List(ctx, q)
|
||||
count, err := s.repo.Count(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, err := s.repo.List(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return res, count, nil
|
||||
}
|
||||
|
||||
func (s *configService) Pear(ctx context.Context) (*dto.PearConfig, error) {
|
||||
|
||||
@ -115,7 +115,17 @@ func (s *departmentService) All(ctx context.Context) ([]*system.Department, erro
|
||||
}
|
||||
|
||||
func (s *departmentService) List(ctx context.Context, q dto.SearchDto) ([]*system.Department, int64, error) {
|
||||
return s.repo.List(ctx, q)
|
||||
count, err := s.repo.Count(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, err := s.repo.List(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return res, count, nil
|
||||
}
|
||||
|
||||
func (s *departmentService) RefreshCache(ctx context.Context) error {
|
||||
|
||||
@ -25,7 +25,17 @@ func (s *loginLogService) Create(ctx context.Context, req *system.LoginLog) erro
|
||||
}
|
||||
|
||||
func (s *loginLogService) List(ctx context.Context, q dto.SearchDto) ([]*system.LoginLog, int64, error) {
|
||||
return s.repo.List(ctx, q)
|
||||
count, err := s.repo.Count(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, err := s.repo.List(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return res, count, nil
|
||||
}
|
||||
|
||||
func (s *loginLogService) LoginTime(ctx context.Context, email string) (dto.LoginTimeDto, error) {
|
||||
@ -45,7 +55,9 @@ func (s *loginLogService) LoginTime(ctx context.Context, email string) (dto.Logi
|
||||
}
|
||||
|
||||
func (s *loginLogService) LoginCount(ctx context.Context, email string) int64 {
|
||||
count, err := s.repo.Count(ctx, email)
|
||||
count, err := s.repo.Count(ctx, dto.SearchDto{
|
||||
SearchEmail: email,
|
||||
})
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/erpserver/model/view"
|
||||
"management/internal/erpserver/repository"
|
||||
"management/internal/erpserver/service/util"
|
||||
"management/internal/erpserver/service/v1"
|
||||
"management/internal/pkg/know"
|
||||
@ -178,15 +179,15 @@ func (s *menuService) MenuViewData(ctx context.Context, roleID int32) ([]*dto.Se
|
||||
|
||||
func (s *menuService) SetRoleMenu(ctx context.Context, roleID int32, rms []*system.RoleMenu) error {
|
||||
// 开启事务
|
||||
return s.Tx.Transaction(ctx, func(ctx context.Context) error {
|
||||
return repository.Transaction(ctx, s.Log, func(c context.Context) error {
|
||||
// 先删除该角色的所有权限
|
||||
err := s.roleMenuService.DeleteByRoleID(ctx, roleID)
|
||||
err := s.roleMenuService.DeleteByRoleID(c, roleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 再添加该角色的所有权限
|
||||
return s.roleMenuService.Create(ctx, rms)
|
||||
return s.roleMenuService.Create(c, rms)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -118,7 +118,17 @@ func (s *roleService) All(ctx context.Context) ([]*system.Role, error) {
|
||||
}
|
||||
|
||||
func (s *roleService) List(ctx context.Context, q dto.SearchDto) ([]*system.Role, int64, error) {
|
||||
return s.repo.List(ctx, q)
|
||||
count, err := s.repo.Count(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, err := s.repo.List(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return res, count, nil
|
||||
}
|
||||
|
||||
func (s *roleService) RefreshCache(ctx context.Context) error {
|
||||
|
||||
@ -22,22 +22,25 @@ import (
|
||||
|
||||
type userService struct {
|
||||
*v1.Service
|
||||
repo system.UserRepository
|
||||
roleService v1.RoleService
|
||||
loginLogService v1.LoginLogService
|
||||
repo system.UserRepository
|
||||
roleService v1.RoleService
|
||||
departmentService v1.DepartmentService
|
||||
loginLogService v1.LoginLogService
|
||||
}
|
||||
|
||||
func NewUserService(
|
||||
service *v1.Service,
|
||||
repo system.UserRepository,
|
||||
roleService v1.RoleService,
|
||||
departmentService v1.DepartmentService,
|
||||
loginLogService v1.LoginLogService,
|
||||
) v1.UserService {
|
||||
return &userService{
|
||||
Service: service,
|
||||
repo: repo,
|
||||
roleService: roleService,
|
||||
loginLogService: loginLogService,
|
||||
Service: service,
|
||||
repo: repo,
|
||||
roleService: roleService,
|
||||
departmentService: departmentService,
|
||||
loginLogService: loginLogService,
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +114,22 @@ func (s *userService) All(ctx context.Context) ([]*system.User, error) {
|
||||
}
|
||||
|
||||
func (s *userService) List(ctx context.Context, q dto.SearchDto) ([]*system.User, int64, error) {
|
||||
return s.repo.List(ctx, q)
|
||||
count, err := s.repo.Count(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
res, err := s.repo.List(ctx, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
for _, user := range res {
|
||||
user.Role, _ = s.roleService.Get(ctx, user.RoleID)
|
||||
user.Department, _ = s.departmentService.Get(ctx, user.DepartmentID)
|
||||
}
|
||||
|
||||
return res, count, nil
|
||||
}
|
||||
|
||||
func (s *userService) Get(ctx context.Context, id int32) (*system.User, error) {
|
||||
|
||||
@ -1,196 +1,198 @@
|
||||
package mid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
//import (
|
||||
// "context"
|
||||
// "errors"
|
||||
// "net/http"
|
||||
// "sync"
|
||||
// "time"
|
||||
//
|
||||
// systemmodel "management/internal/erpserver/model/system"
|
||||
// v1 "management/internal/erpserver/service/v1"
|
||||
// "management/internal/pkg/know"
|
||||
// "management/internal/pkg/session"
|
||||
//
|
||||
// "github.com/drhin/logger"
|
||||
// "go.uber.org/zap"
|
||||
//)
|
||||
//
|
||||
//// AuditBuffer 审计日志缓冲器
|
||||
//type AuditBuffer struct {
|
||||
// auditLogService v1.AuditLogService
|
||||
// log *logger.Logger
|
||||
// buffer chan *systemmodel.AuditLog
|
||||
// stopCh chan struct{}
|
||||
// wg sync.WaitGroup
|
||||
// batchSize int
|
||||
// flushInterval time.Duration
|
||||
//}
|
||||
//
|
||||
//// NewAuditBuffer 创建审计日志缓冲器
|
||||
//func NewAuditBuffer(auditLogService v1.AuditLogService, log *logger.Logger) *AuditBuffer {
|
||||
// return &AuditBuffer{
|
||||
// auditLogService: auditLogService,
|
||||
// log: log,
|
||||
// buffer: make(chan *systemmodel.AuditLog, 10000), // 缓冲区大小
|
||||
// stopCh: make(chan struct{}),
|
||||
// batchSize: 50, // 批量大小
|
||||
// flushInterval: 3 * time.Second, // 刷新间隔
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Start 启动缓冲器
|
||||
//func (ab *AuditBuffer) Start() {
|
||||
// ab.wg.Add(1)
|
||||
// go ab.processBuffer()
|
||||
//}
|
||||
//
|
||||
//// Stop 停止缓冲器
|
||||
//func (ab *AuditBuffer) Stop() {
|
||||
// close(ab.stopCh)
|
||||
// ab.wg.Wait()
|
||||
// close(ab.buffer)
|
||||
//}
|
||||
//
|
||||
//// Add 添加审计日志到缓冲区
|
||||
//func (ab *AuditBuffer) Add(auditLog *systemmodel.AuditLog) {
|
||||
// select {
|
||||
// case ab.buffer <- auditLog:
|
||||
// // 成功添加到缓冲区
|
||||
// default:
|
||||
// // 缓冲区满,记录警告但不阻塞
|
||||
// ab.log.Warn("审计日志缓冲区已满,丢弃日志")
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// processBuffer 处理缓冲区中的日志
|
||||
//func (ab *AuditBuffer) processBuffer() {
|
||||
// defer ab.wg.Done()
|
||||
//
|
||||
// ticker := time.NewTicker(ab.flushInterval)
|
||||
// defer ticker.Stop()
|
||||
//
|
||||
// batch := make([]*systemmodel.AuditLog, 0, ab.batchSize)
|
||||
//
|
||||
// flushBatch := func() {
|
||||
// if len(batch) == 0 {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
// defer cancel()
|
||||
//
|
||||
// // 批量插入
|
||||
// if err := ab.batchInsert(ctx, batch); err != nil {
|
||||
// ab.log.Error("批量插入审计日志失败", err, zap.Int("count", len(batch)))
|
||||
// } else {
|
||||
// ab.log.Debug("批量插入审计日志成功", zap.Int("count", len(batch)))
|
||||
// }
|
||||
//
|
||||
// // 清空批次
|
||||
// batch = batch[:0]
|
||||
// }
|
||||
//
|
||||
// for {
|
||||
// select {
|
||||
// case <-ab.stopCh:
|
||||
// // 停止信号,处理剩余的日志
|
||||
// for len(ab.buffer) > 0 {
|
||||
// select {
|
||||
// case auditLog := <-ab.buffer:
|
||||
// batch = append(batch, auditLog)
|
||||
// if len(batch) >= ab.batchSize {
|
||||
// flushBatch()
|
||||
// }
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// flushBatch() // 处理最后一批
|
||||
// return
|
||||
//
|
||||
// case <-ticker.C:
|
||||
// // 定时刷新
|
||||
// flushBatch()
|
||||
//
|
||||
// case auditLog := <-ab.buffer:
|
||||
// // 收到新的审计日志
|
||||
// batch = append(batch, auditLog)
|
||||
// if len(batch) >= ab.batchSize {
|
||||
// flushBatch()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// batchInsert 批量插入数据库
|
||||
//func (ab *AuditBuffer) batchInsert(ctx context.Context, auditLogs []*systemmodel.AuditLog) error {
|
||||
// maxRetries := 3
|
||||
// for i := 0; i < maxRetries; i++ {
|
||||
// // 假设你的服务有批量创建方法,如果没有,需要添加
|
||||
// if err := ab.auditLogService.BatchCreate(ctx, auditLogs); err != nil {
|
||||
// if i == maxRetries-1 {
|
||||
// return err
|
||||
// }
|
||||
// ab.log.Error("批量插入失败,准备重试", err, zap.Int("retry", i+1))
|
||||
// time.Sleep(time.Duration(i+1) * time.Second)
|
||||
// continue
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//// 全局缓冲器实例
|
||||
//var globalAuditBuffer *AuditBuffer
|
||||
//
|
||||
//// InitAuditBuffer 初始化全局缓冲器
|
||||
//func InitAuditBuffer(auditLogService v1.AuditLogService, log *logger.Logger) {
|
||||
// globalAuditBuffer = NewAuditBuffer(auditLogService, log)
|
||||
// globalAuditBuffer.Start()
|
||||
//}
|
||||
//
|
||||
//// StopAuditBuffer 停止全局缓冲器
|
||||
//func StopAuditBuffer() {
|
||||
// if globalAuditBuffer != nil {
|
||||
// globalAuditBuffer.Stop()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Audit 优化后的中间件
|
||||
//func Audit(sess session.Manager, log *logger.Logger) func(http.Handler) http.Handler {
|
||||
// return func(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// start := time.Now()
|
||||
//
|
||||
// // 提前获取用户信息
|
||||
// user, err := sess.GetUser(r.Context(), know.StoreName)
|
||||
// if err != nil {
|
||||
// log.Error("获取用户会话失败", err)
|
||||
// next.ServeHTTP(w, r)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // 处理请求
|
||||
// next.ServeHTTP(w, r)
|
||||
//
|
||||
// // 异步添加到缓冲区
|
||||
// go func() {
|
||||
// if user.ID == 0 {
|
||||
// log.Error("用户信息为空", errors.New("user is empty"))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// auditLog := systemmodel.NewAuditLog(r, user.Email, user.OS, user.Browser, start, time.Now())
|
||||
//
|
||||
// // 添加到缓冲区,不会阻塞
|
||||
// if globalAuditBuffer != nil {
|
||||
// globalAuditBuffer.Add(auditLog)
|
||||
// }
|
||||
// }()
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
|
||||
systemmodel "management/internal/erpserver/model/system"
|
||||
v1 "management/internal/erpserver/service/v1"
|
||||
"management/internal/pkg/know"
|
||||
"management/internal/pkg/session"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// AuditBuffer 审计日志缓冲器
|
||||
type AuditBuffer struct {
|
||||
auditLogService v1.AuditLogService
|
||||
log *logger.Logger
|
||||
buffer chan *systemmodel.AuditLog
|
||||
stopCh chan struct{}
|
||||
wg sync.WaitGroup
|
||||
batchSize int
|
||||
flushInterval time.Duration
|
||||
}
|
||||
|
||||
// NewAuditBuffer 创建审计日志缓冲器
|
||||
func NewAuditBuffer(auditLogService v1.AuditLogService, log *logger.Logger) *AuditBuffer {
|
||||
return &AuditBuffer{
|
||||
auditLogService: auditLogService,
|
||||
log: log,
|
||||
buffer: make(chan *systemmodel.AuditLog, 10000), // 缓冲区大小
|
||||
stopCh: make(chan struct{}),
|
||||
batchSize: 50, // 批量大小
|
||||
flushInterval: 3 * time.Second, // 刷新间隔
|
||||
}
|
||||
}
|
||||
|
||||
// Start 启动缓冲器
|
||||
func (ab *AuditBuffer) Start() {
|
||||
ab.wg.Add(1)
|
||||
go ab.processBuffer()
|
||||
}
|
||||
|
||||
// Stop 停止缓冲器
|
||||
func (ab *AuditBuffer) Stop() {
|
||||
close(ab.stopCh)
|
||||
ab.wg.Wait()
|
||||
close(ab.buffer)
|
||||
}
|
||||
|
||||
// Add 添加审计日志到缓冲区
|
||||
func (ab *AuditBuffer) Add(auditLog *systemmodel.AuditLog) {
|
||||
select {
|
||||
case ab.buffer <- auditLog:
|
||||
// 成功添加到缓冲区
|
||||
default:
|
||||
// 缓冲区满,记录警告但不阻塞
|
||||
ab.log.Warn("审计日志缓冲区已满,丢弃日志")
|
||||
}
|
||||
}
|
||||
|
||||
// processBuffer 处理缓冲区中的日志
|
||||
func (ab *AuditBuffer) processBuffer() {
|
||||
defer ab.wg.Done()
|
||||
|
||||
ticker := time.NewTicker(ab.flushInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
batch := make([]*systemmodel.AuditLog, 0, ab.batchSize)
|
||||
|
||||
flushBatch := func() {
|
||||
if len(batch) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 批量插入
|
||||
if err := ab.batchInsert(ctx, batch); err != nil {
|
||||
ab.log.Error("批量插入审计日志失败", err, zap.Int("count", len(batch)))
|
||||
} else {
|
||||
ab.log.Debug("批量插入审计日志成功", zap.Int("count", len(batch)))
|
||||
}
|
||||
|
||||
// 清空批次
|
||||
batch = batch[:0]
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ab.stopCh:
|
||||
// 停止信号,处理剩余的日志
|
||||
for len(ab.buffer) > 0 {
|
||||
select {
|
||||
case auditLog := <-ab.buffer:
|
||||
batch = append(batch, auditLog)
|
||||
if len(batch) >= ab.batchSize {
|
||||
flushBatch()
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
flushBatch() // 处理最后一批
|
||||
return
|
||||
|
||||
case <-ticker.C:
|
||||
// 定时刷新
|
||||
flushBatch()
|
||||
|
||||
case auditLog := <-ab.buffer:
|
||||
// 收到新的审计日志
|
||||
batch = append(batch, auditLog)
|
||||
if len(batch) >= ab.batchSize {
|
||||
flushBatch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// batchInsert 批量插入数据库
|
||||
func (ab *AuditBuffer) batchInsert(ctx context.Context, auditLogs []*systemmodel.AuditLog) error {
|
||||
maxRetries := 3
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
// 假设你的服务有批量创建方法,如果没有,需要添加
|
||||
if err := ab.auditLogService.BatchCreate(ctx, auditLogs); err != nil {
|
||||
if i == maxRetries-1 {
|
||||
return err
|
||||
}
|
||||
ab.log.Error("批量插入失败,准备重试", err, zap.Int("retry", i+1))
|
||||
time.Sleep(time.Duration(i+1) * time.Second)
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 全局缓冲器实例
|
||||
var globalAuditBuffer *AuditBuffer
|
||||
|
||||
// InitAuditBuffer 初始化全局缓冲器
|
||||
func InitAuditBuffer(auditLogService v1.AuditLogService, log *logger.Logger) {
|
||||
globalAuditBuffer = NewAuditBuffer(auditLogService, log)
|
||||
globalAuditBuffer.Start()
|
||||
}
|
||||
|
||||
// StopAuditBuffer 停止全局缓冲器
|
||||
func StopAuditBuffer() {
|
||||
if globalAuditBuffer != nil {
|
||||
globalAuditBuffer.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// Audit 优化后的中间件
|
||||
func Audit(sess session.Manager, log *logger.Logger) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
// 提前获取用户信息
|
||||
user, err := sess.GetUser(r.Context(), know.StoreName)
|
||||
if err != nil {
|
||||
log.Error("获取用户会话失败", err)
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理请求
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
// 异步添加到缓冲区
|
||||
go func() {
|
||||
if user.ID == 0 {
|
||||
log.Error("用户信息为空", errors.New("user is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
auditLog := systemmodel.NewAuditLog(r, user.Email, user.OS, user.Browser, start, time.Now())
|
||||
|
||||
// 添加到缓冲区,不会阻塞
|
||||
if globalAuditBuffer != nil {
|
||||
globalAuditBuffer.Add(auditLog)
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
}
|
||||
// ======================================================
|
||||
|
||||
// 如果你的AuditLogService没有BatchCreate方法,需要添加这个接口
|
||||
// 在你的service接口中添加:
|
||||
|
||||
57
internal/pkg/mid/audit_v3.go
Normal file
57
internal/pkg/mid/audit_v3.go
Normal file
@ -0,0 +1,57 @@
|
||||
package mid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
"management/internal/pkg/know"
|
||||
"management/internal/pkg/session"
|
||||
"management/internal/tasks"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/hibiken/asynq"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Audit 改造后的中间件
|
||||
func Audit(sess session.Manager, log *logger.Logger, task tasks.TaskDistributor) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
start := time.Now()
|
||||
|
||||
user, err := sess.GetUser(ctx, know.StoreName)
|
||||
if err != nil {
|
||||
log.Error("获取用户会话失败", err)
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
if user.ID == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
payload := &tasks.PayloadConsumeAuditLog{
|
||||
AuditLog: system.NewAuditLog(r, user.Email, user.OS, user.Browser, start, time.Now()),
|
||||
}
|
||||
|
||||
opts := []asynq.Option{
|
||||
asynq.MaxRetry(10),
|
||||
asynq.ProcessIn(1 * time.Second),
|
||||
asynq.Queue(tasks.QueueCritical),
|
||||
}
|
||||
|
||||
c, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := task.DistributeTaskConsumeAuditLog(c, payload, opts...); err != nil {
|
||||
log.Error("distribute task failed", err,
|
||||
zap.String("type", "audit"),
|
||||
zap.Any("payload", payload),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -7,41 +7,39 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
v1 "management/internal/erpserver/service/v1"
|
||||
"management/internal/pkg/know"
|
||||
"management/internal/pkg/session"
|
||||
|
||||
"github.com/json-iterator/go"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
// 高性能JSON库(全局初始化)
|
||||
var json = jsoniter.ConfigFastest
|
||||
|
||||
// 使用jsoniter优化菜单结构体序列化
|
||||
func init() {
|
||||
jsoniter.RegisterTypeEncoderFunc("dto.OwnerMenuDto",
|
||||
func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
m := (*dto.OwnerMenuDto)(ptr)
|
||||
stream.WriteObjectStart()
|
||||
stream.WriteObjectField("id")
|
||||
stream.WriteUint(uint(m.ID))
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("url")
|
||||
stream.WriteString(m.Url)
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("parentId")
|
||||
stream.WriteUint(uint(m.ParentID))
|
||||
stream.WriteMore()
|
||||
stream.WriteObjectField("isList")
|
||||
stream.WriteBool(m.IsList)
|
||||
stream.WriteObjectEnd()
|
||||
}, nil)
|
||||
}
|
||||
//// 高性能JSON库(全局初始化)
|
||||
//var json = jsoniter.ConfigFastest
|
||||
//
|
||||
//// 使用jsoniter优化菜单结构体序列化
|
||||
//func init() {
|
||||
// jsoniter.RegisterTypeEncoderFunc("dto.OwnerMenuDto",
|
||||
// func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
// m := (*dto.OwnerMenuDto)(ptr)
|
||||
// stream.WriteObjectStart()
|
||||
// stream.WriteObjectField("id")
|
||||
// stream.WriteUint(uint(m.ID))
|
||||
// stream.WriteMore()
|
||||
// stream.WriteObjectField("url")
|
||||
// stream.WriteString(m.Url)
|
||||
// stream.WriteMore()
|
||||
// stream.WriteObjectField("parentId")
|
||||
// stream.WriteUint(uint(m.ParentID))
|
||||
// stream.WriteMore()
|
||||
// stream.WriteObjectField("isList")
|
||||
// stream.WriteBool(m.IsList)
|
||||
// stream.WriteObjectEnd()
|
||||
// }, nil)
|
||||
//}
|
||||
|
||||
var publicRoutes = map[string]bool{
|
||||
"/home.html": true,
|
||||
|
||||
95
internal/pkg/mid/authorize_v6.go
Normal file
95
internal/pkg/mid/authorize_v6.go
Normal file
@ -0,0 +1,95 @@
|
||||
package mid
|
||||
|
||||
//var publicRoutes = map[string]bool{
|
||||
// "/home.html": true,
|
||||
// "/dashboard": true,
|
||||
// "/system/menus": true,
|
||||
// "/upload/img": true,
|
||||
// "/upload/file": true,
|
||||
// "/upload/multi_files": true,
|
||||
// "/system/pear.json": true,
|
||||
// "/logout": true,
|
||||
//}
|
||||
//
|
||||
//var m sync.Map
|
||||
//
|
||||
//func Authorize(
|
||||
// sess session.Manager,
|
||||
// menuService v1.MenuService,
|
||||
//) func(http.Handler) http.Handler {
|
||||
// return func(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// ctx := r.Context()
|
||||
// path := r.URL.Path
|
||||
//
|
||||
// // 登陆检查
|
||||
// user, err := sess.GetUser(ctx, know.StoreName)
|
||||
// if err != nil || user.ID == 0 {
|
||||
// http.Redirect(w, r, "/", http.StatusFound)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // 公共路由放行
|
||||
// if publicRoutes[path] {
|
||||
// ctx = setUser(ctx, user)
|
||||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// n1 := time.Now()
|
||||
// // 权限检查
|
||||
// var menus map[string]*dto.OwnerMenuDto
|
||||
// cacheKey := fmt.Sprintf("user_menus:%d", user.RoleID)
|
||||
// if value, ok := m.Load(cacheKey); ok {
|
||||
// menus = value.(map[string]*dto.OwnerMenuDto)
|
||||
// log.Printf("map (from cache): %s", time.Since(n1).String())
|
||||
// } else {
|
||||
// menus, err = menuService.ListByRoleIDToMap(ctx, user.RoleID)
|
||||
// if err == nil {
|
||||
// m.Store(cacheKey, menus)
|
||||
// }
|
||||
// log.Printf("map (from DB, then cached): %s", time.Since(n1).String())
|
||||
// }
|
||||
//
|
||||
// if !hasPermission(menus, path) {
|
||||
// http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// cur := getCurrentMenus(menus, path)
|
||||
//
|
||||
// ctx = setUser(ctx, user)
|
||||
// ctx = setCurMenus(ctx, cur)
|
||||
//
|
||||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func hasPermission(menus map[string]*dto.OwnerMenuDto, path string) bool {
|
||||
// _, ok := menus[path]
|
||||
// return ok
|
||||
//}
|
||||
//
|
||||
//func getCurrentMenus(data map[string]*dto.OwnerMenuDto, path string) []dto.OwnerMenuDto {
|
||||
// var res []dto.OwnerMenuDto
|
||||
//
|
||||
// menu, ok := data[path]
|
||||
// if !ok {
|
||||
// return res
|
||||
// }
|
||||
//
|
||||
// for _, item := range data {
|
||||
// if menu.IsList {
|
||||
// if item.ParentID == menu.ID || item.ID == menu.ID {
|
||||
// res = append(res, *item)
|
||||
// }
|
||||
// } else {
|
||||
// if item.ParentID == menu.ParentID {
|
||||
// res = append(res, *item)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return res
|
||||
//}
|
||||
@ -2,8 +2,10 @@ package mid
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"management/internal/erpserver/model/dto"
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
@ -73,3 +75,19 @@ func GetHtmlCsrfToken(ctx context.Context) templ.Component {
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
type trkey struct{}
|
||||
|
||||
func setTran(ctx context.Context, tx sqldb.CommitRollbacker) context.Context {
|
||||
return context.WithValue(ctx, trkey{}, tx)
|
||||
}
|
||||
|
||||
// GetTran retrieves the value that can manage a transaction.
|
||||
func GetTran(ctx context.Context) (sqldb.CommitRollbacker, error) {
|
||||
v, ok := ctx.Value(trkey{}).(sqldb.CommitRollbacker)
|
||||
if !ok {
|
||||
return nil, errors.New("transaction not found in context")
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
57
internal/pkg/mid/transaction.go
Normal file
57
internal/pkg/mid/transaction.go
Normal file
@ -0,0 +1,57 @@
|
||||
package mid
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"management/internal/pkg/sqldb"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func BeginCommitRollback(log *logger.Logger, bgn sqldb.Beginner) func(http.Handler) http.Handler {
|
||||
m := func(next http.Handler) http.Handler {
|
||||
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
hasCommitted := false
|
||||
|
||||
log.Info("BEGIN TRANSACTION")
|
||||
tx, err := bgn.Begin()
|
||||
if err != nil {
|
||||
log.Error("BEGIN TRANSACTION", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if !hasCommitted {
|
||||
log.Info("ROLLBACK TRANSACTION")
|
||||
}
|
||||
|
||||
if err := tx.Rollback(); err != nil {
|
||||
if errors.Is(err, sql.ErrTxDone) {
|
||||
return
|
||||
}
|
||||
log.Info("ROLLBACK TRANSACTION", zap.Error(err))
|
||||
}
|
||||
}()
|
||||
|
||||
ctx := r.Context()
|
||||
ctx = setTran(ctx, tx)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
|
||||
log.Info("COMMIT TRANSACTION")
|
||||
if err := tx.Commit(); err != nil {
|
||||
log.Error("COMMIT TRANSACTION", err)
|
||||
return
|
||||
}
|
||||
|
||||
hasCommitted = true
|
||||
})
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
214
internal/pkg/sqldb/sqldb.go
Normal file
214
internal/pkg/sqldb/sqldb.go
Normal file
@ -0,0 +1,214 @@
|
||||
package sqldb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"management/internal/pkg/config"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
uniqueViolation = "23505"
|
||||
undefinedTable = "42P01"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDBNotFound = sql.ErrNoRows
|
||||
ErrDBDuplicatedEntry = errors.New("duplicated entry")
|
||||
ErrUndefinedTable = errors.New("undefined table")
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
User string
|
||||
Password string
|
||||
Host string
|
||||
Port int
|
||||
Name string
|
||||
MaxIdleConns int
|
||||
MaxOpenConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
ConnMaxIdleTime time.Duration
|
||||
}
|
||||
|
||||
func NewDB(config *config.Config, log *logger.Logger) (*sqlx.DB, func(), error) {
|
||||
dsn := fmt.Sprintf("postgresql://%s:%s@%s:%d/%s?sslmode=disable",
|
||||
config.DB.Username,
|
||||
config.DB.Password,
|
||||
config.DB.Host,
|
||||
config.DB.Port,
|
||||
config.DB.DBName,
|
||||
)
|
||||
|
||||
db, err := sqlx.Open("pgx", dsn)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("sqlx open db: %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := db.PingContext(ctx); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 设置最大空闲连接数(默认 2)
|
||||
db.SetMaxIdleConns(config.DB.MaxIdleConns)
|
||||
|
||||
// 设置最大打开连接数(默认 0 无限制)
|
||||
db.SetMaxOpenConns(config.DB.MaxOpenConns)
|
||||
|
||||
// 设置连接最大存活时间
|
||||
db.SetConnMaxLifetime(config.DB.ConnMaxLifetime)
|
||||
|
||||
// 设置连接最大空闲时间
|
||||
db.SetConnMaxIdleTime(config.DB.ConnMaxIdleTime)
|
||||
|
||||
cleanup := func() {
|
||||
if err := db.Close(); err != nil {
|
||||
log.Error("sql db close error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return db, cleanup, nil
|
||||
}
|
||||
|
||||
func NamedExecContext(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data any) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
switch data.(type) {
|
||||
case struct{}:
|
||||
log.Error("database.NamedExecContext (data is struct)", err,
|
||||
zap.String("query", query),
|
||||
zap.Any("ERROR", err))
|
||||
default:
|
||||
log.Error("database.NamedExecContext", err,
|
||||
zap.String("query", query),
|
||||
zap.Any("ERROR", err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := sqlx.NamedExecContext(ctx, db, query, data); err != nil {
|
||||
var pgError *pgconn.PgError
|
||||
if errors.As(err, &pgError) {
|
||||
switch pgError.Code {
|
||||
case undefinedTable:
|
||||
return ErrUndefinedTable
|
||||
case uniqueViolation:
|
||||
return ErrDBDuplicatedEntry
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NamedQueryStruct(ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data any, dest any) (err error) {
|
||||
q := queryString(query, data)
|
||||
rows, err := sqlx.NamedQueryContext(ctx, db, q, data)
|
||||
if err != nil {
|
||||
var pqErr *pgconn.PgError
|
||||
if errors.As(err, &pqErr) && pqErr.Code == undefinedTable {
|
||||
return ErrUndefinedTable
|
||||
}
|
||||
log.Error("NamedQueryStruct NamedQueryContext error", err,
|
||||
zap.String("query", q),
|
||||
zap.Any("data", data),
|
||||
)
|
||||
return err
|
||||
}
|
||||
defer func(rows *sqlx.Rows) {
|
||||
err := rows.Close()
|
||||
if err != nil {
|
||||
log.Error("rows close error", err)
|
||||
}
|
||||
}(rows)
|
||||
|
||||
if !rows.Next() {
|
||||
return ErrDBNotFound
|
||||
}
|
||||
|
||||
if err := rows.StructScan(dest); err != nil {
|
||||
log.Error("NamedQueryStruct StructScan error", err,
|
||||
zap.String("query", q),
|
||||
zap.Any("data", data),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NamedQuerySlice[T any](ctx context.Context, log *logger.Logger, db sqlx.ExtContext, query string, data any, dest *[]T) (err error) {
|
||||
q := queryString(query, data)
|
||||
rows, err := sqlx.NamedQueryContext(ctx, db, q, data)
|
||||
if err != nil {
|
||||
var pqErr *pgconn.PgError
|
||||
if errors.As(err, &pqErr) && pqErr.Code == undefinedTable {
|
||||
return ErrUndefinedTable
|
||||
}
|
||||
log.Error("NamedQueryStruct NamedQueryContext error", err,
|
||||
zap.String("query", q),
|
||||
zap.Any("data", data),
|
||||
)
|
||||
return err
|
||||
}
|
||||
defer func(rows *sqlx.Rows) {
|
||||
err := rows.Close()
|
||||
if err != nil {
|
||||
log.Error("rows close error", err)
|
||||
}
|
||||
}(rows)
|
||||
|
||||
var slice []T
|
||||
for rows.Next() {
|
||||
v := new(T)
|
||||
if err := rows.StructScan(v); err != nil {
|
||||
log.Error("NamedQuerySlice StructScan error", err,
|
||||
zap.String("query", q),
|
||||
zap.Any("data", data),
|
||||
)
|
||||
return err
|
||||
}
|
||||
slice = append(slice, *v)
|
||||
}
|
||||
*dest = slice
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryString(query string, args any) string {
|
||||
query, params, err := sqlx.Named(query, args)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
for _, param := range params {
|
||||
var value string
|
||||
switch v := param.(type) {
|
||||
case string:
|
||||
value = fmt.Sprintf("'%s'", v)
|
||||
case []byte:
|
||||
value = fmt.Sprintf("'%s'", string(v))
|
||||
default:
|
||||
value = fmt.Sprintf("%v", v)
|
||||
}
|
||||
query = strings.Replace(query, "?", value, 1)
|
||||
}
|
||||
|
||||
query = strings.ReplaceAll(query, "\t", "")
|
||||
query = strings.ReplaceAll(query, "\n", " ")
|
||||
|
||||
return strings.Trim(query, " ")
|
||||
}
|
||||
49
internal/pkg/sqldb/tran.go
Normal file
49
internal/pkg/sqldb/tran.go
Normal file
@ -0,0 +1,49 @@
|
||||
package sqldb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// Beginner represents a value that can begin a transaction.
|
||||
type Beginner interface {
|
||||
Begin() (CommitRollbacker, error)
|
||||
}
|
||||
|
||||
// CommitRollbacker represents a value that can commit or rollback a transaction.
|
||||
type CommitRollbacker interface {
|
||||
Commit() error
|
||||
Rollback() error
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// DBBeginner implements the Beginner interface,
|
||||
type DBBeginner struct {
|
||||
sqlxDB *sqlx.DB
|
||||
}
|
||||
|
||||
// NewBeginner constructs a value that implements the beginner interface.
|
||||
func NewBeginner(sqlxDB *sqlx.DB) *DBBeginner {
|
||||
return &DBBeginner{
|
||||
sqlxDB: sqlxDB,
|
||||
}
|
||||
}
|
||||
|
||||
// Begin implements the Beginner interface and returns a concrete value that
|
||||
// implements the CommitRollbacker interface.
|
||||
func (db *DBBeginner) Begin() (CommitRollbacker, error) {
|
||||
return db.sqlxDB.Beginx()
|
||||
}
|
||||
|
||||
// GetExtContext is a helper function that extracts the sqlx value
|
||||
// from the domain transactor interface for transactional use.
|
||||
func GetExtContext(tx CommitRollbacker) (sqlx.ExtContext, error) {
|
||||
ec, ok := tx.(sqlx.ExtContext)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Transactor(%T) not of a type *sql.Tx", tx)
|
||||
}
|
||||
|
||||
return ec, nil
|
||||
}
|
||||
21
internal/tasks/asynq.go
Normal file
21
internal/tasks/asynq.go
Normal file
@ -0,0 +1,21 @@
|
||||
package tasks
|
||||
|
||||
import "github.com/redis/go-redis/v9"
|
||||
|
||||
// RedisClientConnector 是一个适配器,它包装了现有的 redis.Client
|
||||
// 并实现了 asynq.RedisConnOpt 接口。
|
||||
type RedisClientConnector struct {
|
||||
Client *redis.Client
|
||||
}
|
||||
|
||||
func NewRedisClientConnector(c *redis.Client) *RedisClientConnector {
|
||||
return &RedisClientConnector{
|
||||
Client: c,
|
||||
}
|
||||
}
|
||||
|
||||
// MakeRedisClient 实现了 asynq.RedisConnOpt 接口。
|
||||
// 它直接返回已存在的客户端实例。
|
||||
func (c *RedisClientConnector) MakeRedisClient() interface{} {
|
||||
return c.Client
|
||||
}
|
||||
59
internal/tasks/audit.go
Normal file
59
internal/tasks/audit.go
Normal file
@ -0,0 +1,59 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"management/internal/erpserver/model/system"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
const TaskConsumeAuditLog = "task:audit"
|
||||
|
||||
type PayloadConsumeAuditLog struct {
|
||||
*system.AuditLog
|
||||
}
|
||||
|
||||
func (d *RedisTaskDistributor) DistributeTaskConsumeAuditLog(
|
||||
ctx context.Context,
|
||||
payload *PayloadConsumeAuditLog,
|
||||
opts ...asynq.Option,
|
||||
) error {
|
||||
jsonPayload, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal task payload: %w", err)
|
||||
}
|
||||
|
||||
task := asynq.NewTask(TaskConsumeAuditLog, jsonPayload, opts...)
|
||||
_, err = d.client.EnqueueContext(ctx, task)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enqueue task: %w", err)
|
||||
}
|
||||
|
||||
//d.log.Info("enqueued task",
|
||||
// zap.String("type", task.Type()),
|
||||
// zap.Binary("payload", task.Payload()),
|
||||
// zap.String("queue", info.Queue),
|
||||
// zap.Int("max_retry", info.MaxRetry),
|
||||
//)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *RedisTaskProcessor) ProcessTaskConsumeAuditLog(ctx context.Context, task *asynq.Task) error {
|
||||
var payload PayloadConsumeAuditLog
|
||||
if err := json.Unmarshal(task.Payload(), &payload); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal payload: %w", asynq.SkipRetry)
|
||||
}
|
||||
|
||||
if err := p.auditService.Create(ctx, payload.AuditLog); err != nil {
|
||||
return fmt.Errorf("failed to process task: %w", err)
|
||||
}
|
||||
|
||||
//p.log.Info("processed task",
|
||||
// zap.String("type", task.Type()),
|
||||
// zap.Binary("payload", task.Payload()),
|
||||
//)
|
||||
return nil
|
||||
}
|
||||
25
internal/tasks/distributor.go
Normal file
25
internal/tasks/distributor.go
Normal file
@ -0,0 +1,25 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/hibiken/asynq"
|
||||
)
|
||||
|
||||
type TaskDistributor interface {
|
||||
DistributeTaskConsumeAuditLog(ctx context.Context, payload *PayloadConsumeAuditLog, opts ...asynq.Option) error
|
||||
}
|
||||
|
||||
type RedisTaskDistributor struct {
|
||||
log *logger.Logger
|
||||
client *asynq.Client
|
||||
}
|
||||
|
||||
func NewRedisTaskDistributor(log *logger.Logger, opt *RedisClientConnector) TaskDistributor {
|
||||
client := asynq.NewClient(opt)
|
||||
return &RedisTaskDistributor{
|
||||
log: log,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
65
internal/tasks/processor.go
Normal file
65
internal/tasks/processor.go
Normal file
@ -0,0 +1,65 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "management/internal/erpserver/service/v1"
|
||||
|
||||
"github.com/drhin/logger"
|
||||
"github.com/hibiken/asynq"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
QueueCritical = "critical"
|
||||
QueueDefault = "default"
|
||||
)
|
||||
|
||||
type TaskProcessor interface {
|
||||
Start() error
|
||||
Shutdown()
|
||||
|
||||
ProcessTaskConsumeAuditLog(ctx context.Context, task *asynq.Task) error
|
||||
}
|
||||
|
||||
type RedisTaskProcessor struct {
|
||||
log *logger.Logger
|
||||
server *asynq.Server
|
||||
auditService v1.AuditLogService
|
||||
}
|
||||
|
||||
func NewRedisTaskProcessor(log *logger.Logger, opt *RedisClientConnector, auditService v1.AuditLogService) TaskProcessor {
|
||||
server := asynq.NewServer(
|
||||
opt,
|
||||
asynq.Config{
|
||||
Queues: map[string]int{
|
||||
QueueCritical: 10,
|
||||
QueueDefault: 5,
|
||||
},
|
||||
ErrorHandler: asynq.ErrorHandlerFunc(func(ctx context.Context, task *asynq.Task, err error) {
|
||||
log.Error("process task failed", err,
|
||||
zap.String("type", task.Type()),
|
||||
zap.Binary("payload", task.Payload()),
|
||||
)
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
return &RedisTaskProcessor{
|
||||
log: log,
|
||||
server: server,
|
||||
auditService: auditService,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *RedisTaskProcessor) Start() error {
|
||||
mux := asynq.NewServeMux()
|
||||
|
||||
mux.HandleFunc(TaskConsumeAuditLog, p.ProcessTaskConsumeAuditLog)
|
||||
|
||||
return p.server.Start(mux)
|
||||
}
|
||||
|
||||
func (p *RedisTaskProcessor) Shutdown() {
|
||||
p.server.Shutdown()
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user