diff --git a/.dockerignore b/.dockerignore index 68a6593..1c720a2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,10 +1,16 @@ -log/ - -LICENSE - *.exe main -testpaper +management + +configs/ +upload/ +doc/ +logs/ +log/ modd.conf -*.yaml \ No newline at end of file +*.yaml +LICENSE +README.md +Makefile +.gitignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8f3f017..48b0cc0 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ go.work *.log *.yaml -!config.dev.yaml +!configs/config.dev.yaml !sqlc.yaml upload/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile index f8fd5eb..d1a861b 100644 --- a/Makefile +++ b/Makefile @@ -39,10 +39,13 @@ db_schema: sqlc: sqlc generate +wire: + wire ./... + test: go test -v -cover ./... server: modd -.PHONY: network redis postgres createdb dropdb psql migrateinit migrateup migratedown db_docs db_schema test server \ No newline at end of file +.PHONY: network redis postgres createdb dropdb psql migrateinit migrateup migratedown db_docs db_schema wire test server \ No newline at end of file diff --git a/cmd/cmd.go b/cmd/cmd.go deleted file mode 100644 index ccfed89..0000000 --- a/cmd/cmd.go +++ /dev/null @@ -1,29 +0,0 @@ -package cmd - -import ( - "log" - "reflect" - "runtime" -) - -type server interface { - ListenAndServe() error -} - -func mustInit(fn func() error) { - err := fn() - if err != nil { - ptr := reflect.ValueOf(fn).Pointer() - fi := runtime.FuncForPC(ptr) - log.Fatalf("%s failed: %v", fi.Name(), err) - } -} - -func mustInitAny[T any](s T, fn func(s T) error) { - err := fn(s) - if err != nil { - ptr := reflect.ValueOf(fn).Pointer() - fi := runtime.FuncForPC(ptr) - log.Fatalf("%s(T) failed: %v", fi.Name(), err) - } -} diff --git a/cmd/erp.go b/cmd/erp.go index 8f24672..7743953 100644 --- a/cmd/erp.go +++ b/cmd/erp.go @@ -1,156 +1,94 @@ package cmd import ( - "context" "fmt" "log" + "net/http" + "runtime" + "time" - "management/internal/erpserver/handler" - "management/internal/erpserver/repository" - systemrepo "management/internal/erpserver/repository/system" - commonservice "management/internal/erpserver/service/v1/common" - systemservice "management/internal/erpserver/service/v1/system" - "management/internal/pkg/binding" + "management/internal/erpserver" "management/internal/pkg/config" - "management/internal/pkg/database" - "management/internal/pkg/middleware" - "management/internal/pkg/redis" - "management/internal/pkg/session" - "management/internal/pkg/tpl" "github.com/drhin/logger" + "github.com/fvbock/endless" "github.com/spf13/cobra" ) +var configPath string + +// erpCmd 代表erp命令 var erpCmd = &cobra.Command{ Use: "erp", - Short: "Start erp management server", - Long: `A Service to erp management`, + Short: "启动 ERP 管理服务器", + Long: `启动 ERP 管理服务器,可根据指定配置文件加载服务配置。 + +示例: + # 使用默认配置文件启动服务 + ./management erp + + # 使用自定义配置文件启动服务 + ./management erp -c config.prod.yaml + +该命令会初始化配置、日志系统,然后启动 HTTP 服务器监听指定端口。在 Windows 系统上使用标准的 http.Server,其他系统使用 endless 实现热重启。 +`, Run: func(cmd *cobra.Command, args []string) { - err := runErp(cmd.Context()) - if err != nil { + if err := runErp(); err != nil { log.Fatalf("run erp failed: %v", err) } }, } -func init() { - erpCmd.Flags().StringVarP(&configPath, "config", "c", "", "Custom config file path") - rootCmd.AddCommand(erpCmd) -} - -func runErp(ctx context.Context) error { - conf, err := config.New(configPath) - checkError(err) - - // 初始化数据 - // dbinit.InitSeed() - - // mustInit(redis.Init) - - err = binding.SetValidatorTrans("zh") - checkError(err) - - contextx, err := newAppContext(conf) - checkError(err) - - address := fmt.Sprintf("%s:%d", conf.App.Host, conf.App.Port) - log.Printf("Starting erp manage server on %s", address) - server := InitServer(address, contextx.Router()) - return server.ListenAndServe() -} - -func newAppContext(conf *config.Config) (*handler.AppContext, error) { - // initialize DB - dbOptions := &database.PostgreSQLOptions{ - Addr: conf.DB.Host, - Username: conf.DB.Username, - Password: conf.DB.Password, - Database: conf.DB.DBName, - } - gdb, err := database.NewPostgreSQL(dbOptions) - if err != nil { - return nil, err - } - repo := repository.NewStore(gdb) - - // initialize Redis - redis, err := redis.New(conf.Redis) - if err != nil { - return nil, err - } - - // initialize Session - db, err := gdb.DB() - if err != nil { - return nil, err - } - session := session.New(db, conf.App.Prod) - - // initialize Logger - log, err := logger.NewProduction() - if err != nil { - return nil, err - } - - ctx := &handler.AppContext{ - DB: gdb, - Store: repo, - Redis: redis, - Session: session, - Config: conf, - Log: log, - } - - setRepository(ctx) - setService(ctx) - setMiddleware(ctx) - if err := setRender(ctx); err != nil { - return nil, err - } - - return ctx, nil -} - -func setRepository(ctx *handler.AppContext) { - ctx.UserRepo = systemrepo.NewUserRepository(ctx.Store) - ctx.LoginLogRepo = systemrepo.NewLoginLogRepository(ctx.Store) - ctx.AuditLogRepo = systemrepo.NewAuditLogRepository(ctx.Store) - ctx.RoleRepo = systemrepo.NewRoleRepository(ctx.Store) - ctx.DepartmentRepo = systemrepo.NewDepartmentRepository(ctx.Store) - ctx.ConfigRepo = systemrepo.NewConfigRepository(ctx.Store) - ctx.RoleMenuRepo = systemrepo.NewRoleMenuRepository(ctx.Store) - ctx.MenuRepo = systemrepo.NewMenuRepository(ctx.Store) -} - -func setService(ctx *handler.AppContext) { - ctx.CaptchaService = commonservice.NewCaptchaService() - - ctx.ConfigService = systemservice.NewConfigService(ctx.ConfigRepo, ctx.Redis) - ctx.LoginLogService = systemservice.NewLoginLogService(ctx.LoginLogRepo) - ctx.AuditLogService = systemservice.NewAuditLogService(ctx.AuditLogRepo) - ctx.RoleService = systemservice.NewRoleService(ctx.RoleRepo) - ctx.UserService = systemservice.NewUserService(ctx.Session, ctx.Log, ctx.UserRepo, ctx.RoleService, ctx.LoginLogService) - ctx.DepartmentService = systemservice.NewDepartmentService(ctx.DepartmentRepo) - ctx.RoleMenuService = systemservice.NewRoleMenuService(ctx.RoleMenuRepo) - ctx.MenuService = systemservice.NewMenuService(ctx.Redis, ctx.Store, ctx.MenuRepo, ctx.RoleService, ctx.RoleMenuService) -} - -func setMiddleware(ctx *handler.AppContext) { - ctx.Middleware = middleware.New(ctx.Session, ctx.MenuService, ctx.AuditLogService) -} - -func setRender(ctx *handler.AppContext) error { - var err error - ctx.Render, err = tpl.New(ctx.Session, ctx.MenuService) +func runErp() error { + config, err := config.New(configPath) if err != nil { return err } - return nil -} -func checkError(err error) { + l, err := logger.NewDevelopment() if err != nil { - log.Fatalf("init failed: %v", err) + return err + } + + mux, fn, err := erpserver.NewWire(config, l) + if err != nil { + return err + } + + defer fn() + + address := fmt.Sprintf("%s:%d", config.App.Host, config.App.Port) + log.Printf("Starting manage server on %s", address) + + if runtime.GOOS == "windows" { + s := &http.Server{ + Addr: address, + Handler: mux, + ReadTimeout: 20 * time.Second, + WriteTimeout: 20 * time.Second, + MaxHeaderBytes: 1 << 20, + } + return s.ListenAndServe() + } else { + s := endless.NewServer(address, mux) + s.ReadHeaderTimeout = 20 * time.Second + s.WriteTimeout = 20 * time.Second + s.MaxHeaderBytes = 1 << 20 + return s.ListenAndServe() } } + +func init() { + rootCmd.AddCommand(erpCmd) + + // 在这里,您将定义您的标志和配置设置。 + + // Cobra支持适用于此命令的持久标志 + // 以及所有子命令,例如: + // erpCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra支持仅在执行以下命令时运行的本地标志 + // 直接调用,例如: + // erpCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + erpCmd.Flags().StringVarP(&configPath, "config", "c", "configs/config.dev.yaml", "custom config file") +} diff --git a/cmd/root.go b/cmd/root.go index c173b2c..b824b80 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,25 +1,48 @@ package cmd import ( - "fmt" "os" "github.com/spf13/cobra" ) -var configPath string - +// rootCmd 表示在没有任何子命令的情况下调用的基本命令 var rootCmd = &cobra.Command{ - Use: "testpaper", - Short: "Start testpaper server", - Long: `testpaper service`, - Run: func(cmd *cobra.Command, args []string) { - }, + Use: "management", + Short: "ERP 管理系统命令行工具", + Long: `ERP 管理系统命令行工具,用于管理和控制 ERP 管理服务器。支持多种操作,如启动服务器、根据不同配置文件加载服务等。 + +示例: + # 使用默认配置文件启动 ERP 管理服务器 + ./management erp + + # 使用自定义配置文件启动 ERP 管理服务器 + ./management erp -c config.prod.yaml + +该工具依赖 Cobra 库构建,在 Windows 系统上使用标准的 http.Server 启动服务,其他系统使用 endless 库实现热重启。 +`, + // 如果您的裸应用程序没有注释以下行 + // 具有与之关联的操作: + // Run: func(cmd *cobra.Command, args []string) { }, } +// Execute 将所有子命令添加到根命令中,并相应地设置标志。 +// 这是由main.main()调用的。rootCmd只需要发生一次。 func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) + err := rootCmd.Execute() + if err != nil { os.Exit(1) } } + +func init() { + // 在这里,您将定义您的标志和配置设置。 + // Cobra支持持久标志,如果在这里定义, + // 将为您的应用程序提供全球支持。 + + // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.management.yaml)") + + // Cobra还支持仅运行本地标志 + // 当直接调用此操作时。 + rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/server_other.go b/cmd/server_other.go deleted file mode 100644 index ee1bdf2..0000000 --- a/cmd/server_other.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !windows -// +build !windows - -package cmd - -import ( - "net/http" - "time" - - "github.com/fvbock/endless" -) - -func InitServer(address string, router http.Handler) server { - s := endless.NewServer(address, router) - s.ReadHeaderTimeout = 20 * time.Second - s.WriteTimeout = 20 * time.Second - s.MaxHeaderBytes = 1 << 20 - return s -} diff --git a/cmd/server_win.go b/cmd/server_win.go deleted file mode 100644 index 5d593a0..0000000 --- a/cmd/server_win.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build windows -// +build windows - -package cmd - -import ( - "net/http" - "time" -) - -func InitServer(address string, router http.Handler) server { - return &http.Server{ - Addr: address, - Handler: router, - ReadTimeout: 20 * time.Second, - WriteTimeout: 20 * time.Second, - MaxHeaderBytes: 1 << 20, - } -} diff --git a/config.dev.yaml b/configs/config.dev.yaml similarity index 95% rename from config.dev.yaml rename to configs/config.dev.yaml index 7bf015c..230c922 100644 --- a/config.dev.yaml +++ b/configs/config.dev.yaml @@ -1,8 +1,8 @@ app: host: 0.0.0.0 port: 9001 + prod: false db: - driver: postgres host: 127.0.0.1 port: 5432 username: root diff --git a/Dockerfile b/deploys/Dockerfile similarity index 96% rename from Dockerfile rename to deploys/Dockerfile index 9fc2a16..7b77aa3 100644 --- a/Dockerfile +++ b/deploys/Dockerfile @@ -3,7 +3,7 @@ FROM golang:1.24.0-alpine3.21 AS builder ENV GO111MODULE=on \ GOPROXY=https://goproxy.cn,direct WORKDIR /app -COPY ../ . +COPY ../.. . RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o testpaper main.go # Run Stage diff --git a/go.mod b/go.mod index 0956c0d..6d3a13f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/alexedwards/scs/pgxstore v0.0.0-20250212122300-421ef1d8611c github.com/alexedwards/scs/v2 v2.8.0 github.com/bwmarrin/snowflake v0.3.0 - github.com/drhin/logger v0.0.0-20250407025020-f3e0beed768b + github.com/drhin/logger v0.0.0-20250417021954-aa33afe047bc github.com/fsnotify/fsnotify v1.8.0 github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 github.com/gin-gonic/gin v1.10.0 @@ -56,6 +56,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/google/wire v0.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect diff --git a/go.sum b/go.sum index 9c85783..758dcbc 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/drhin/logger v0.0.0-20250407005450-5bbde7825366 h1:HcCQ0kAGAiprUVYDw5 github.com/drhin/logger v0.0.0-20250407005450-5bbde7825366/go.mod h1:XfunbMqKGMTCr3jsu5Vwe7kVUAINPOi/4z+e8qvVJDE= github.com/drhin/logger v0.0.0-20250407025020-f3e0beed768b h1:VkXK9/bFutQXrFYcHcbxzkvMHAP9dybMJRMLEMPtKro= github.com/drhin/logger v0.0.0-20250407025020-f3e0beed768b/go.mod h1:QUg+qnn7zvYONlsRRGWVKI4zArm4vZN2e5JFTydJICM= +github.com/drhin/logger v0.0.0-20250417021954-aa33afe047bc h1:/vyhHw4e3eNwpEEJ80m889cVcLV7g+AiTVKzRgCl6As= +github.com/drhin/logger v0.0.0-20250417021954-aa33afe047bc/go.mod h1:QUg+qnn7zvYONlsRRGWVKI4zArm4vZN2e5JFTydJICM= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= @@ -77,11 +79,15 @@ github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17w github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 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/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= @@ -233,6 +239,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh 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.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +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.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= @@ -243,6 +250,7 @@ golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs 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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -252,6 +260,7 @@ 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.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= @@ -278,6 +287,7 @@ golang.org/x/sys v0.6.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.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= @@ -289,6 +299,7 @@ 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.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +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= @@ -307,6 +318,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn 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.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= diff --git a/internal/erpserver/handler/app.go b/internal/erpserver/handler/app.go deleted file mode 100644 index fcf9f22..0000000 --- a/internal/erpserver/handler/app.go +++ /dev/null @@ -1,62 +0,0 @@ -package handler - -import ( - systemrepo "management/internal/erpserver/model/system" - "management/internal/erpserver/repository" - systemsvc "management/internal/erpserver/service/v1" - "management/internal/pkg/config" - "management/internal/pkg/middleware" - "management/internal/pkg/redis" - "management/internal/pkg/session" - "management/internal/pkg/tpl" - - "github.com/drhin/logger" - "gorm.io/gorm" -) - -type AppContext struct { - // DB - DB *gorm.DB - - // Store - Store repository.Store - - // Cache - Redis redis.RedisCache - - // session - Session session.Session - - // middleware - Middleware middleware.Middleware - - // render - Render tpl.Renderer - - // config - Config *config.Config - - // log - Log *logger.Logger - - // repository - UserRepo systemrepo.UserRepository - LoginLogRepo systemrepo.LoginLogRepository - AuditLogRepo systemrepo.AuditLogRepository - RoleRepo systemrepo.RoleRepository - DepartmentRepo systemrepo.DepartmentRepository - ConfigRepo systemrepo.ConfigRepository - MenuRepo systemrepo.MenuRepository - RoleMenuRepo systemrepo.RoleMenuRepository - - // service - CaptchaService systemsvc.CaptchaService - UserService systemsvc.UserService - LoginLogService systemsvc.LoginLogService - AuditLogService systemsvc.AuditLogService - RoleService systemsvc.RoleService - DepartmentService systemsvc.DepartmentService - ConfigService systemsvc.ConfigService - MenuService systemsvc.MenuService - RoleMenuService systemsvc.RoleMenuService -} diff --git a/internal/erpserver/handler/common/captcha.go b/internal/erpserver/handler/common/captcha.go index 2bee1a3..2ea789c 100644 --- a/internal/erpserver/handler/common/captcha.go +++ b/internal/erpserver/handler/common/captcha.go @@ -3,22 +3,20 @@ package common import ( "net/http" + "management/internal/erpserver/handler" v1 "management/internal/erpserver/service/v1" - "management/internal/pkg/config" - "management/internal/pkg/tpl" + "management/internal/pkg/render" ) -type captchaHandler struct { - conf *config.Captcha - render tpl.Renderer - svc v1.CaptchaService +type CaptchaHandler struct { + *handler.Handler + captchaService v1.CaptchaService } -func NewCaptchaHandler(conf *config.Captcha, render tpl.Renderer, svc v1.CaptchaService) *captchaHandler { - return &captchaHandler{ - conf: conf, - render: render, - svc: svc, +func NewCaptchaHandler(handler *handler.Handler, captchaService v1.CaptchaService) *CaptchaHandler { + return &CaptchaHandler{ + Handler: handler, + captchaService: captchaService, } } @@ -29,20 +27,22 @@ type CaptchaResponse struct { OpenCaptcha int `json:"open_captcha"` } -func (h *captchaHandler) Captcha(w http.ResponseWriter, r *http.Request) { - keyLong := h.conf.KeyLong - oc := h.conf.OpenCaptcha - id, b64s, _, err := h.svc.Generate(h.conf.ImgHeight, h.conf.ImgWidth, keyLong, 0.7, 80) +func (h *CaptchaHandler) Captcha(w http.ResponseWriter, _ *http.Request) { + id, b64s, _, err := h.captchaService.Generate( + h.Config.Captcha.ImgHeight, + h.Config.Captcha.ImgWidth, + h.Config.Captcha.KeyLong, + 0.7, 80) if err != nil { - h.render.JSON(w, tpl.Response{Success: false, Message: "获取验证码失败"}) + h.JSONErr(w, "获取验证码失败") return } rsp := CaptchaResponse{ CaptchaID: id, PicPath: b64s, - CaptchaLength: keyLong, - OpenCaptcha: oc, + CaptchaLength: h.Config.Captcha.KeyLong, + OpenCaptcha: h.Config.Captcha.OpenCaptcha, } - h.render.JSON(w, tpl.Response{Success: true, Message: "ok", Data: rsp}) + h.JSON(w, render.Response{Success: true, Message: "ok", Data: rsp}) } diff --git a/internal/erpserver/handler/common/upload.go b/internal/erpserver/handler/common/upload.go index 8f382c1..530323e 100644 --- a/internal/erpserver/handler/common/upload.go +++ b/internal/erpserver/handler/common/upload.go @@ -1,59 +1,70 @@ package common import ( + "io" "mime/multipart" "net/http" + "management/internal/erpserver/handler" fileutil "management/internal/pkg/file" - "management/internal/pkg/tpl" ) -type uploadHandler struct { - render tpl.Renderer +type UploadHandler struct { + *handler.Handler } -func NewUploadHandler(render tpl.Renderer) *uploadHandler { - return &uploadHandler{ - render: render, +func NewUploadHandler(handler *handler.Handler) *UploadHandler { + return &UploadHandler{ + Handler: handler, } } const maxImageSize = 100 << 20 // 100 MB -func (h *uploadHandler) Img(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() +func (h *UploadHandler) Img(w http.ResponseWriter, r *http.Request) { + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + h.Log.Error(err.Error(), err) + } + }(r.Body) _, fh, err := r.FormFile("files") if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } path, err := fileutil.UploadFile(fh, fileutil.IMG) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, tpl.Response{Success: true, Message: "ok", Data: path}) + h.JSONObj(w, "ok", path) } -func (h *uploadHandler) File(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() +func (h *UploadHandler) File(w http.ResponseWriter, r *http.Request) { + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + h.Log.Error(err.Error(), err) + } + }(r.Body) _, fh, err := r.FormFile("files") if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } path, err := fileutil.UploadFile(fh, fileutil.ALL) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, tpl.Response{Success: true, Message: "ok", Data: path}) + h.JSONObj(w, "ok", path) } type UploadFileRes struct { @@ -61,8 +72,13 @@ type UploadFileRes struct { Path string `json:"path"` } -func (h *uploadHandler) MutilFiles(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() +func (h *UploadHandler) MultiFiles(w http.ResponseWriter, r *http.Request) { + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + h.Log.Error(err.Error(), err) + } + }(r.Body) err := r.ParseMultipartForm(int64(maxImageSize)) if err != nil { @@ -80,7 +96,7 @@ func (h *uploadHandler) MutilFiles(w http.ResponseWriter, r *http.Request) { filePath, err := fileutil.UploadFile(item, fileutil.ALL) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } @@ -99,5 +115,5 @@ func (h *uploadHandler) MutilFiles(w http.ResponseWriter, r *http.Request) { } } - h.render.JSON(w, tpl.Response{Success: true, Message: "ok", Data: res}) + h.JSONObj(w, "ok", res) } diff --git a/internal/erpserver/handler/handler.go b/internal/erpserver/handler/handler.go new file mode 100644 index 0000000..f94b5ff --- /dev/null +++ b/internal/erpserver/handler/handler.go @@ -0,0 +1,65 @@ +package handler + +import ( + "context" + "net/http" + + "management/internal/erpserver/model/dto" + "management/internal/pkg/config" + "management/internal/pkg/middleware" + "management/internal/pkg/render" + + "github.com/drhin/logger" +) + +type Handler struct { + Config *config.Config + Log *logger.Logger + Middleware middleware.Middleware + + render render.Render +} + +func NewHandler( + config *config.Config, + log *logger.Logger, + middleware middleware.Middleware, + render render.Render, +) *Handler { + return &Handler{ + Config: config, + Log: log, + Middleware: middleware, + render: render, + } +} + +// ===================================================================================================================== +// middleware 帮助方法 + +func (h *Handler) AuthUser(ctx context.Context) dto.AuthorizeUser { + return h.Middleware.AuthUser(ctx) +} + +// ===================================================================================================================== +// render 帮助方法 + +func (h *Handler) HTML(w http.ResponseWriter, r *http.Request, name string, data map[string]any) { + h.render.HTML(w, r, name, data) +} + +func (h *Handler) JSON(w http.ResponseWriter, data any) { + h.render.JSON(w, data) +} + +func (h *Handler) JSONObj(w http.ResponseWriter, message string, data any) { + h.render.JSONObj(w, message, data) +} + +func (h *Handler) JSONOk(w http.ResponseWriter, message string) { + h.render.JSONOk(w, message) +} + +func (h *Handler) JSONErr(w http.ResponseWriter, message string) { + h.render.JSONErr(w, message) +} diff --git a/internal/erpserver/handler/system/audit.go b/internal/erpserver/handler/system/audit.go index 5cf5046..7577539 100644 --- a/internal/erpserver/handler/system/audit.go +++ b/internal/erpserver/handler/system/audit.go @@ -3,28 +3,29 @@ package system import ( "net/http" + "management/internal/erpserver/handler" "management/internal/erpserver/model/dto" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/convertor" - "management/internal/pkg/tpl" + "management/internal/pkg/render" ) -type auditHandler struct { - render tpl.Renderer - svc v1.AuditLogService +type AuditHandler struct { + *handler.Handler + auditLogService v1.AuditLogService } -func NewAuditHandler(render tpl.Renderer, svc v1.AuditLogService) *auditHandler { - return &auditHandler{ - render: render, - svc: svc, +func NewAuditHandler(handler *handler.Handler, auditLogService v1.AuditLogService) *AuditHandler { + return &AuditHandler{ + Handler: handler, + auditLogService: auditLogService, } } -func (h *auditHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *AuditHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "audit_log/list.tmpl", nil) + h.HTML(w, r, "audit_log/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) @@ -32,19 +33,19 @@ func (h *auditHandler) List(w http.ResponseWriter, r *http.Request) { q.SearchEmail = r.PostFormValue("email") q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) - res, count, err := h.svc.List(r.Context(), q) + res, count, err := h.auditLogService.List(r.Context(), q) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: count, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } diff --git a/internal/erpserver/handler/system/config.go b/internal/erpserver/handler/system/config.go index be0172e..e44304c 100644 --- a/internal/erpserver/handler/system/config.go +++ b/internal/erpserver/handler/system/config.go @@ -1,171 +1,163 @@ package system import ( + "encoding/json" "net/http" "strings" + "management/internal/erpserver/handler" "management/internal/erpserver/model/dto" - systemmodel "management/internal/erpserver/model/system" - systemsvc "management/internal/erpserver/service/v1" + systemModel "management/internal/erpserver/model/system" + systemService "management/internal/erpserver/service/v1" "management/internal/pkg/convertor" "management/internal/pkg/database" - "management/internal/pkg/redis" - "management/internal/pkg/tpl" + "management/internal/pkg/render" ) -type configHandler struct { - render tpl.Renderer - redis redis.RedisCache - configsvc systemsvc.ConfigService +type ConfigHandler struct { + *handler.Handler + configService systemService.ConfigService } -func NewConfigHandler(render tpl.Renderer, redis redis.RedisCache, configsvc systemsvc.ConfigService) *configHandler { - return &configHandler{ - render: render, - redis: redis, - configsvc: configsvc, +func NewConfigHandler(handler *handler.Handler, configService systemService.ConfigService) *ConfigHandler { + return &ConfigHandler{ + Handler: handler, + configService: configService, } } -func (h *configHandler) Add(w http.ResponseWriter, r *http.Request) { - h.render.HTML(w, r, "config/edit.tmpl", map[string]any{ - "Item": &systemmodel.Config{}, - "Result": "", - }) -} - -type EditSysConfig struct { - *systemmodel.Config - Result string -} - -func (h *configHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := convertor.QueryInt[int32](vars, "id", 0) - vm := &EditSysConfig{} - if id > 0 { - if conf, err := h.configsvc.Get(r.Context(), id); err == nil { - vm.Config = conf - vm.Result = string(conf.Value) - } - } - h.render.HTML(w, r, "config/edit.tmpl", map[string]any{ - "Item": vm.Config, - "Result": vm.Result, - }) -} - -func (h *configHandler) Save(w http.ResponseWriter, r *http.Request) { - id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0) - key := r.PostFormValue("Key") - value := r.PostFormValue("Value") - if len(key) == 0 { - h.render.JSONERR(w, "Key不能为空") - return - } - if len(value) == 0 { - h.render.JSONERR(w, "Value不能为空") - return - } - - ctx := r.Context() - if id == 0 { - arg := &systemmodel.Config{ - Key: key, - Value: []byte(value), - } - err := h.configsvc.Create(ctx, arg) - if err != nil { - if database.IsUniqueViolation(err) { - h.render.JSONERR(w, "数据已存在") - return - } - h.render.JSONERR(w, err.Error()) - return - } - - h.render.JSONOK(w, "添加成功") - } else { - res, err := h.configsvc.Get(ctx, id) - if err != nil { - h.render.JSONERR(w, err.Error()) - return - } - - res.Value = []byte(value) - err = h.configsvc.Update(ctx, res) - if err != nil { - h.render.JSONERR(w, err.Error()) - return - } - - h.render.JSONOK(w, "更新成功") - } -} - -func (h *configHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *ConfigHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "config/list.tmpl", nil) + h.HTML(w, r, "config/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto + q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) q.SearchName = r.PostFormValue("name") q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) ctx := r.Context() - res, count, err := h.configsvc.List(ctx, q) + res, count, err := h.configService.List(ctx, q) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: count, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } -func (h *configHandler) Refresh(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - err := h.redis.Del(r.Context(), strings.ToLower(key)) - if err != nil { - h.render.JSONERR(w, err.Error()) +func (h *ConfigHandler) Add(w http.ResponseWriter, r *http.Request) { + h.HTML(w, r, "config/edit.tmpl", map[string]any{ + "Item": &systemModel.Config{}, + "Result": "", + }) +} + +type EditSysConfig struct { + *systemModel.Config + Result string +} + +func (h *ConfigHandler) Edit(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + id := convertor.QueryInt[int32](vars, "id", 0) + vm := &EditSysConfig{} + if id > 0 { + if conf, err := h.configService.Get(r.Context(), id); err == nil { + vm.Config = conf + b, _ := json.Marshal(&conf.Value) + vm.Result = string(b) + } + } + h.HTML(w, r, "config/edit.tmpl", map[string]any{ + "Item": vm.Config, + "Result": vm.Result, + }) +} + +func (h *ConfigHandler) Save(w http.ResponseWriter, r *http.Request) { + id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0) + key := r.PostFormValue("Key") + value := r.PostFormValue("Value") + if len(key) == 0 { + h.JSONErr(w, "Key不能为空") + return + } + if len(value) == 0 { + h.JSONErr(w, "Value不能为空") return } - h.render.JSONOK(w, "刷新成功") + ctx := r.Context() + if id == 0 { + arg := &systemModel.Config{ + Key: key, + Value: value, + } + err := h.configService.Create(ctx, arg) + if err != nil { + if database.IsUniqueViolation(err) { + h.JSONErr(w, "数据已存在") + return + } + h.JSONErr(w, err.Error()) + return + } + + h.JSONOk(w, "添加成功") + } else { + res, err := h.configService.Get(ctx, id) + if err != nil { + h.JSONErr(w, err.Error()) + return + } + + res.Value = value + err = h.configService.Update(ctx, res) + if err != nil { + h.JSONErr(w, err.Error()) + return + } + + h.JSONOk(w, "更新成功") + } } -func (h *configHandler) ResetPear(w http.ResponseWriter, r *http.Request) { - // b, err := json.Marshal(pearadmin.PearJson) - // if err != nil { - // h.render.JSONERR(w, err.Error()) - // return - // } +func (h *ConfigHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { + key := r.FormValue("key") + err := h.configService.RefreshCache(r.Context(), strings.ToLower(key)) + if err != nil { + h.JSONErr(w, err.Error()) + return + } - // err = h.configsvc.Update(r.Context(), &db.UpdateSysConfigByKeyParams{ - // Key: pearadmin.PearKey, - // Value: b, - // }) - // if err != nil { - // h.render.JSONERR(w, err.Error()) - // return - // } - h.render.JSONOK(w, "重置成功") + h.JSONOk(w, "刷新成功") } -func (h *configHandler) Pear(w http.ResponseWriter, r *http.Request) { - pear, err := h.configsvc.Pear(r.Context()) +func (h *ConfigHandler) ResetPear(w http.ResponseWriter, r *http.Request) { + err := h.configService.ResetPear(r.Context()) + if err != nil { + h.JSONErr(w, err.Error()) + return + } + h.JSONOk(w, "重置成功") +} + +func (h *ConfigHandler) Pear(w http.ResponseWriter, r *http.Request) { + pear, err := h.configService.Pear(r.Context()) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - h.render.JSON(w, pear) + h.JSON(w, pear) } diff --git a/internal/erpserver/handler/system/department.go b/internal/erpserver/handler/system/department.go index 1e8b37e..31fa473 100644 --- a/internal/erpserver/handler/system/department.go +++ b/internal/erpserver/handler/system/department.go @@ -3,31 +3,32 @@ package system import ( "net/http" + "management/internal/erpserver/handler" "management/internal/erpserver/model/dto" "management/internal/erpserver/model/form" systemmodel "management/internal/erpserver/model/system" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/binding" "management/internal/pkg/convertor" - "management/internal/pkg/tpl" + "management/internal/pkg/render" ) -type departmentHandler struct { - render tpl.Renderer - svc v1.DepartmentService +type DepartmentHandler struct { + *handler.Handler + departmentService v1.DepartmentService } -func NewDepartmentHandler(render tpl.Renderer, svc v1.DepartmentService) *departmentHandler { - return &departmentHandler{ - render: render, - svc: svc, +func NewDepartmentHandler(handler *handler.Handler, departmentService v1.DepartmentService) *DepartmentHandler { + return &DepartmentHandler{ + Handler: handler, + departmentService: departmentService, } } -func (h *departmentHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *DepartmentHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "department/list.tmpl", nil) + h.HTML(w, r, "department/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) @@ -37,117 +38,117 @@ func (h *departmentHandler) List(w http.ResponseWriter, r *http.Request) { q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0) q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) - res, count, err := h.svc.List(r.Context(), q) + res, count, err := h.departmentService.List(r.Context(), q) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: count, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } } -func (h *departmentHandler) Add(w http.ResponseWriter, r *http.Request) { - h.render.HTML(w, r, "department/edit.tmpl", map[string]any{ +func (h *DepartmentHandler) Add(w http.ResponseWriter, r *http.Request) { + h.HTML(w, r, "department/edit.tmpl", map[string]any{ "Item": &systemmodel.Department{Sort: 6666}, }) } -func (h *departmentHandler) AddChildren(w http.ResponseWriter, r *http.Request) { +func (h *DepartmentHandler) AddChildren(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() - parentID := convertor.QueryInt(vars, "parentID", 0) - vm := &systemmodel.Department{ParentID: int32(parentID), Sort: 6666} - h.render.HTML(w, r, "department/edit.tmpl", map[string]any{ + parentID := convertor.QueryInt[int32](vars, "parentID", 0) + vm := &systemmodel.Department{ParentID: parentID, Sort: 6666} + h.HTML(w, r, "department/edit.tmpl", map[string]any{ "Item": vm, }) } -func (h *departmentHandler) Edit(w http.ResponseWriter, r *http.Request) { +func (h *DepartmentHandler) Edit(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() id := convertor.QueryInt[int32](vars, "id", 0) vm := &systemmodel.Department{Sort: 6666} if id > 0 { - vm, _ = h.svc.Get(r.Context(), id) + vm, _ = h.departmentService.Get(r.Context(), id) } - h.render.HTML(w, r, "department/edit.tmpl", map[string]any{ + h.HTML(w, r, "department/edit.tmpl", map[string]any{ "Item": vm, }) } -func (h *departmentHandler) Save(w http.ResponseWriter, r *http.Request) { +func (h *DepartmentHandler) Save(w http.ResponseWriter, r *http.Request) { var req form.Department if err := binding.Form.Bind(r, &req); err != nil { - h.render.JSONERR(w, binding.ValidatorErrors(err)) + h.JSONErr(w, binding.ValidatorErrors(err)) return } ctx := r.Context() if *req.ID == 0 { - err := h.svc.Create(ctx, &req) + err := h.departmentService.Create(ctx, &req) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "添加成功") + h.JSONOk(w, "添加成功") } else { - err := h.svc.Update(ctx, &req) + err := h.departmentService.Update(ctx, &req) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "更新成功") + h.JSONOk(w, "更新成功") } } -func (h *departmentHandler) Data(w http.ResponseWriter, r *http.Request) { +func (h *DepartmentHandler) Data(w http.ResponseWriter, r *http.Request) { ctx := r.Context() vars := r.URL.Query() t := vars.Get("type") if t == "tree" { - res, err := h.svc.Tree(ctx, 0) + res, err := h.departmentService.Tree(ctx, 0) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) - } else if t == "xmselect_tree" { - res, err := h.svc.XmSelectTree(ctx, 0) + h.JSON(w, res) + } else if t == "xm_select_tree" { + res, err := h.departmentService.XmSelectTree(ctx, 0) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) + h.JSON(w, res) } } -func (h *departmentHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { - err := h.svc.RefreshCache(r.Context()) +func (h *DepartmentHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { + err := h.departmentService.RefreshCache(r.Context()) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "缓存刷新成功") + h.JSONOk(w, "缓存刷新成功") } -func (h *departmentHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { +func (h *DepartmentHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - err := h.svc.RebuildParentPath(ctx) + err := h.departmentService.RebuildParentPath(ctx) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "重建成功") + h.JSONOk(w, "重建成功") } diff --git a/internal/erpserver/handler/system/home.go b/internal/erpserver/handler/system/home.go index 7b5345f..51bd896 100644 --- a/internal/erpserver/handler/system/home.go +++ b/internal/erpserver/handler/system/home.go @@ -3,38 +3,39 @@ package system import ( "net/http" + "management/internal/erpserver/handler" v1 "management/internal/erpserver/service/v1" - "management/internal/pkg/middleware" - "management/internal/pkg/tpl" ) -type homeHandler struct { - render tpl.Renderer - mi middleware.Middleware - usersvc v1.UserService - loginLogsvc v1.LoginLogService +type HomeHandler struct { + *handler.Handler + userService v1.UserService + loginLogService v1.LoginLogService } -func NewHomeHandler(render tpl.Renderer, mi middleware.Middleware, usersvc v1.UserService, loginLogsvc v1.LoginLogService) *homeHandler { - return &homeHandler{ - render: render, - mi: mi, - usersvc: usersvc, - loginLogsvc: loginLogsvc, +func NewHomeHandler( + handler *handler.Handler, + userService v1.UserService, + loginLogService v1.LoginLogService, +) *HomeHandler { + return &HomeHandler{ + Handler: handler, + userService: userService, + loginLogService: loginLogService, } } -func (h *homeHandler) Home(w http.ResponseWriter, r *http.Request) { - h.render.HTML(w, r, "home/home.tmpl", nil) +func (h *HomeHandler) Home(w http.ResponseWriter, r *http.Request) { + h.HTML(w, r, "home/home.tmpl", nil) } -func (h *homeHandler) Dashboard(w http.ResponseWriter, r *http.Request) { +func (h *HomeHandler) Dashboard(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - auth := h.mi.AuthUser(ctx) - user, _ := h.usersvc.Get(ctx, auth.ID) - t := h.loginLogsvc.LoginLatestTime(ctx, auth.Email) - c := h.loginLogsvc.LoginCount(ctx, auth.Email) - h.render.HTML(w, r, "home/dashboard.tmpl", map[string]any{ + auth := h.AuthUser(ctx) + user, _ := h.userService.Get(ctx, auth.ID) + t := h.loginLogService.LoginLatestTime(ctx, auth.Email) + c := h.loginLogService.LoginCount(ctx, auth.Email) + h.HTML(w, r, "home/dashboard.tmpl", map[string]any{ "Auth": auth, "User": user, "LastLoginTime": t, diff --git a/internal/erpserver/handler/system/login_log.go b/internal/erpserver/handler/system/login_log.go index da324eb..93bad9f 100644 --- a/internal/erpserver/handler/system/login_log.go +++ b/internal/erpserver/handler/system/login_log.go @@ -3,47 +3,48 @@ package system import ( "net/http" + "management/internal/erpserver/handler" "management/internal/erpserver/model/dto" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/convertor" - "management/internal/pkg/tpl" + "management/internal/pkg/render" ) -type loginLogHandler struct { - render tpl.Renderer - svc v1.LoginLogService +type LoginLogHandler struct { + *handler.Handler + loginLogService v1.LoginLogService } -func NewLoginLogHandler(render tpl.Renderer, svc v1.LoginLogService) *loginLogHandler { - return &loginLogHandler{ - render: render, - svc: svc, +func NewLoginLogHandler(handler *handler.Handler, loginLogService v1.LoginLogService) *LoginLogHandler { + return &LoginLogHandler{ + Handler: handler, + loginLogService: loginLogService, } } -func (h *loginLogHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *LoginLogHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "login_log/list.tmpl", nil) + h.HTML(w, r, "login_log/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) q.SearchEmail = r.PostFormValue("email") q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) - res, count, err := h.svc.List(r.Context(), q) + res, count, err := h.loginLogService.List(r.Context(), q) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: count, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } diff --git a/internal/erpserver/handler/system/menu.go b/internal/erpserver/handler/system/menu.go index 9608508..f92a7b0 100644 --- a/internal/erpserver/handler/system/menu.go +++ b/internal/erpserver/handler/system/menu.go @@ -6,79 +6,76 @@ import ( "strings" "time" + "management/internal/erpserver/handler" systemmodel "management/internal/erpserver/model/system" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/convertor" "management/internal/pkg/database" - "management/internal/pkg/middleware" - "management/internal/pkg/tpl" + "management/internal/pkg/render" "github.com/google/uuid" ) const style = "layui-btn-primary layui-btn-sm" -type menuHandler struct { - render tpl.Renderer - mi middleware.Middleware - - svc v1.MenuService +type MenuHandler struct { + *handler.Handler + menuService v1.MenuService } -func NewMenuHandler(render tpl.Renderer, mi middleware.Middleware, svc v1.MenuService) *menuHandler { - return &menuHandler{ - render: render, - mi: mi, - svc: svc, +func NewMenuHandler(handler *handler.Handler, menuService v1.MenuService) *MenuHandler { + return &MenuHandler{ + Handler: handler, + menuService: menuService, } } -func (h *menuHandler) Menus(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) Menus(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - user := h.mi.AuthUser(ctx) - menus, err := h.svc.OwerMenus(ctx, user.RoleID) + user := h.AuthUser(ctx) + menus, err := h.menuService.OwerMenus(ctx, user.RoleID) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, menus) + h.JSON(w, menus) } -func (h *menuHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "menu/list.tmpl", nil) + h.HTML(w, r, "menu/list.tmpl", nil) case http.MethodPost: - res, err := h.svc.ListMenuTree(r.Context()) + res, err := h.menuService.ListMenuTree(r.Context()) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: 0, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: - h.render.JSONERR(w, "method not allowed") + h.JSONErr(w, "method not allowed") } } -func (h *menuHandler) Add(w http.ResponseWriter, r *http.Request) { - h.render.HTML(w, r, "menu/edit.tmpl", map[string]any{ +func (h *MenuHandler) Add(w http.ResponseWriter, r *http.Request) { + h.HTML(w, r, "menu/edit.tmpl", map[string]any{ "Item": &systemmodel.Menu{Style: style, Visible: true, Sort: 6666}, }) } -func (h *menuHandler) AddChildren(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) AddChildren(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() parentID := convertor.QueryInt[int32](vars, "parentID", 0) - vm := &systemmodel.Menu{ParentID: int32(parentID), Style: style, Visible: true, Sort: 6666} - parent, err := h.svc.Get(r.Context(), parentID) + vm := &systemmodel.Menu{ParentID: parentID, Style: style, Visible: true, Sort: 6666} + parent, err := h.menuService.Get(r.Context(), parentID) if err == nil { if parent.Type == "node" { vm.Type = "menu" @@ -86,27 +83,27 @@ func (h *menuHandler) AddChildren(w http.ResponseWriter, r *http.Request) { vm.Type = "btn" } } - h.render.HTML(w, r, "menu/edit.tmpl", map[string]any{ + h.HTML(w, r, "menu/edit.tmpl", map[string]any{ "Item": vm, }) } -func (h *menuHandler) Edit(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) Edit(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() id := convertor.QueryInt[int32](vars, "id", 0) vm := &systemmodel.Menu{Style: style, Sort: 6666} if id > 0 { - vm, _ = h.svc.Get(r.Context(), id) + vm, _ = h.menuService.Get(r.Context(), id) } - h.render.HTML(w, r, "menu/edit.tmpl", map[string]any{ + h.HTML(w, r, "menu/edit.tmpl", map[string]any{ "Item": vm, }) } -func (h *menuHandler) Save(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) Save(w http.ResponseWriter, r *http.Request) { id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0) name := r.PostFormValue("Name") - dispalyName := r.PostFormValue("DisplayName") + displayName := r.PostFormValue("DisplayName") t := r.PostFormValue("Type") url := r.PostFormValue("Url") if len(url) == 0 { @@ -127,9 +124,9 @@ func (h *menuHandler) Save(w http.ResponseWriter, r *http.Request) { parentPath := "" if parentID > 0 { - parent, err := h.svc.Get(ctx, parentID) + parent, err := h.menuService.Get(ctx, parentID) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } parentPath = parent.ParentPath + "," + strconv.Itoa(int(parentID)) + "," @@ -139,10 +136,10 @@ func (h *menuHandler) Save(w http.ResponseWriter, r *http.Request) { if id == 0 { arg := &systemmodel.Menu{ Name: name, - DisplayName: dispalyName, + DisplayName: displayName, Url: url, Type: t, - ParentID: int32(parentID), + ParentID: parentID, ParentPath: parentPath, Avatar: avatar, Style: style, @@ -153,26 +150,26 @@ func (h *menuHandler) Save(w http.ResponseWriter, r *http.Request) { CreatedAt: time.Now(), UpdatedAt: time.Now(), } - err := h.svc.Create(ctx, arg) + err := h.menuService.Create(ctx, arg) if err != nil { if database.IsUniqueViolation(err) { - h.render.JSONERR(w, "菜单已存在") + h.JSONErr(w, "菜单已存在") return } - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "添加成功") + h.JSONOk(w, "添加成功") } else { - res, err := h.svc.Get(ctx, id) + res, err := h.menuService.Get(ctx, id) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } res.Name = name - res.DisplayName = dispalyName + res.DisplayName = displayName res.Url = url res.Type = t res.ParentID = parentID @@ -184,48 +181,48 @@ func (h *menuHandler) Save(w http.ResponseWriter, r *http.Request) { res.Status = status res.Sort = sort res.UpdatedAt = time.Now() - err = h.svc.Update(ctx, res) + err = h.menuService.Update(ctx, res) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "更新成功") + h.JSONOk(w, "更新成功") } } -func (h *menuHandler) Data(w http.ResponseWriter, r *http.Request) { +func (h *MenuHandler) Data(w http.ResponseWriter, r *http.Request) { ctx := r.Context() vars := r.URL.Query() t := vars.Get("type") if t == "tree" { - res, err := h.svc.Tree(ctx, 0) + res, err := h.menuService.Tree(ctx, 0) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) + h.JSON(w, res) return - } else if t == "xmselect_tree" { - res, err := h.svc.XmSelectTree(ctx, 0) + } else if t == "xm_select_tree" { + res, err := h.menuService.XmSelectTree(ctx, 0) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) + h.JSON(w, res) return } - h.render.JSON(w, nil) + h.JSON(w, nil) } -func (h *menuHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { - err := h.svc.RefreshCache(r.Context()) +func (h *MenuHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { + err := h.menuService.RefreshCache(r.Context()) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "缓存刷新成功") + h.JSONOk(w, "缓存刷新成功") } diff --git a/internal/erpserver/handler/system/role.go b/internal/erpserver/handler/system/role.go index 20d5013..8a43020 100644 --- a/internal/erpserver/handler/system/role.go +++ b/internal/erpserver/handler/system/role.go @@ -4,33 +4,34 @@ import ( "net/http" "strings" + "management/internal/erpserver/handler" "management/internal/erpserver/model/dto" "management/internal/erpserver/model/form" "management/internal/erpserver/model/system" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/binding" "management/internal/pkg/convertor" - "management/internal/pkg/tpl" + "management/internal/pkg/render" ) -type roleHandler struct { - render tpl.Renderer - svc v1.RoleService - menusvc v1.MenuService +type RoleHandler struct { + *handler.Handler + roleService v1.RoleService + menuService v1.MenuService } -func NewRoleHandler(render tpl.Renderer, svc v1.RoleService, menusvc v1.MenuService) *roleHandler { - return &roleHandler{ - render: render, - svc: svc, - menusvc: menusvc, +func NewRoleHandler(handler *handler.Handler, roleService v1.RoleService, menuService v1.MenuService) *RoleHandler { + return &RoleHandler{ + Handler: handler, + roleService: roleService, + menuService: menuService, } } -func (h *roleHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "role/list.tmpl", nil) + h.HTML(w, r, "role/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) @@ -40,159 +41,163 @@ func (h *roleHandler) List(w http.ResponseWriter, r *http.Request) { q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0) q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) - res, count, err := h.svc.List(r.Context(), q) + res, count, err := h.roleService.List(r.Context(), q) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: count, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } } -func (h *roleHandler) Add(w http.ResponseWriter, r *http.Request) { - h.render.HTML(w, r, "role/edit.tmpl", map[string]any{ +func (h *RoleHandler) Add(w http.ResponseWriter, r *http.Request) { + h.HTML(w, r, "role/edit.tmpl", map[string]any{ "Item": &system.Role{Sort: 6666}, }) } -func (h *roleHandler) AddChildren(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) AddChildren(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() - parentID := convertor.QueryInt(vars, "parentID", 0) - vm := &system.Role{ParentID: int32(parentID), Sort: 6666} - h.render.HTML(w, r, "role/edit.tmpl", map[string]any{ + parentID := convertor.QueryInt[int32](vars, "parentID", 0) + vm := &system.Role{ParentID: parentID, Sort: 6666} + h.HTML(w, r, "role/edit.tmpl", map[string]any{ "Item": vm, }) } -func (h *roleHandler) Edit(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) Edit(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() id := convertor.QueryInt[int32](vars, "id", 0) vm := &system.Role{Sort: 6666} if id > 0 { - vm, _ = h.svc.Get(r.Context(), id) + vm, _ = h.roleService.Get(r.Context(), id) } - h.render.HTML(w, r, "role/edit.tmpl", map[string]any{ + h.HTML(w, r, "role/edit.tmpl", map[string]any{ "Item": vm, }) } -func (h *roleHandler) Save(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) Save(w http.ResponseWriter, r *http.Request) { var req form.Role if err := binding.Form.Bind(r, &req); err != nil { - h.render.JSONERR(w, binding.ValidatorErrors(err)) + h.JSONErr(w, binding.ValidatorErrors(err)) return } ctx := r.Context() if *req.ID == 0 { - err := h.svc.Create(ctx, &req) + err := h.roleService.Create(ctx, &req) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "添加成功") + h.JSONOk(w, "添加成功") } else { - err := h.svc.Update(ctx, &req) + err := h.roleService.Update(ctx, &req) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "更新成功") + h.JSONOk(w, "更新成功") } } -func (h *roleHandler) Data(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) Data(w http.ResponseWriter, r *http.Request) { ctx := r.Context() vars := r.URL.Query() t := vars.Get("type") if t == "tree" { - res, err := h.svc.Tree(ctx, 0) + res, err := h.roleService.Tree(ctx, 0) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) + h.JSON(w, res) return - } else if t == "xmselect_tree" { - res, err := h.svc.XmSelectTree(ctx, 0) + } else if t == "xm_select_tree" { + res, err := h.roleService.XmSelectTree(ctx, 0) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) + h.JSON(w, res) return } } -func (h *roleHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { - err := h.svc.RefreshCache(r.Context()) +func (h *RoleHandler) RefreshCache(w http.ResponseWriter, r *http.Request) { + err := h.roleService.RefreshCache(r.Context()) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "缓存刷新成功") + h.JSONOk(w, "缓存刷新成功") } -func (h *roleHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - err := h.svc.RebuildParentPath(ctx) + err := h.roleService.RebuildParentPath(ctx) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "重建成功") + h.JSONOk(w, "重建成功") } -func (h *roleHandler) RefreshRoleMenus(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) RefreshRoleMenus(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // 获取需要刷新的角色ID roleID := convertor.ConvertInt[int32](r.PostFormValue("roleID"), 0) - role, err := h.svc.Get(ctx, int32(roleID)) - if err != nil || role == nil { - h.render.JSONERR(w, err.Error()) + role, err := h.roleService.Get(ctx, roleID) + if err != nil { + h.JSONErr(w, err.Error()) + return + } + if role == nil { + h.JSONErr(w, "角色不存在") return } // 刷新角色菜单 (角色所拥有的菜单集合) - _, err = h.menusvc.SetListByRoleID(ctx, role.ID) + _, err = h.menuService.SetListByRoleID(ctx, role.ID) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } // 刷新角色菜单 (角色所拥有的菜单集合) - _, err = h.menusvc.SetListByRoleIDToMap(ctx, role.ID) + _, err = h.menuService.SetListByRoleIDToMap(ctx, role.ID) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } // 刷新角色菜单树 (pear admin layui 使用的格式) - _, err = h.menusvc.SetOwerMenus(ctx, role.ID) + _, err = h.menuService.SetOwerMenus(ctx, role.ID) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "刷新成功") + h.JSONOk(w, "刷新成功") } -func (h *roleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { +func (h *RoleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: vars := r.URL.Query() @@ -204,36 +209,36 @@ func (h *roleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { if id > 0 { ctx := r.Context() var err error - vm.Role, err = h.svc.Get(ctx, id) + vm.Role, err = h.roleService.Get(ctx, id) if err == nil { - vm.Menus, _ = h.menusvc.MenuViewData(ctx, vm.Role.ID) + vm.Menus, _ = h.menuService.MenuViewData(ctx, vm.Role.ID) } } - h.render.HTML(w, r, "role/set_menu.tmpl", map[string]any{ + h.HTML(w, r, "role/set_menu.tmpl", map[string]any{ "Item": vm, }) case http.MethodPost: ctx := r.Context() id := convertor.ConvertInt[int32](r.PostFormValue("ID"), 0) if id == 0 { - h.render.JSONERR(w, "角色异常, 请刷新重试") + h.JSONErr(w, "角色异常, 请刷新重试") return } - role, err := h.svc.Get(ctx, id) + role, err := h.roleService.Get(ctx, id) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } menus := r.PostFormValue("roleMenu") if len(menus) == 0 { - h.render.JSONERR(w, "请选择菜单") + h.JSONErr(w, "请选择菜单") return } menuArr := strings.Split(menus, ",") if len(menuArr) == 0 { - h.render.JSONERR(w, "请选择菜单") + h.JSONErr(w, "请选择菜单") return } @@ -241,9 +246,9 @@ func (h *roleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { for _, v := range menuArr { menuID := convertor.ConvertInt(v, 0) if menuID > 0 { - menu, err := h.menusvc.Get(ctx, int32(menuID)) + menu, err := h.menuService.Get(ctx, int32(menuID)) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } @@ -255,17 +260,17 @@ func (h *roleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { } if len(rms) == 0 { - h.render.JSONERR(w, "请选择正确的菜单") + h.JSONErr(w, "请选择正确的菜单") return } - err = h.menusvc.SetRoleMenu(ctx, id, rms) + err = h.menuService.SetRoleMenu(ctx, id, rms) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "设置成功") + h.JSONOk(w, "设置成功") default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } diff --git a/internal/erpserver/handler/system/user.go b/internal/erpserver/handler/system/user.go index 26527dd..921da2e 100644 --- a/internal/erpserver/handler/system/user.go +++ b/internal/erpserver/handler/system/user.go @@ -1,117 +1,113 @@ package system import ( + "io" "log" "net/http" + "management/internal/erpserver/handler" "management/internal/erpserver/model/dto" "management/internal/erpserver/model/form" systemmodel "management/internal/erpserver/model/system" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/binding" "management/internal/pkg/convertor" - "management/internal/pkg/middleware" - "management/internal/pkg/tpl" - "management/internal/pkg/tpl/html" + "management/internal/pkg/render" + "management/internal/pkg/render/html" ) -// userHandler 是 UserHandler 接口的实现. -type userHandler struct { - render tpl.Renderer - mi middleware.Middleware - - captchasvc v1.CaptchaService - usersvc v1.UserService - rolesvc v1.RoleService - departmentsvc v1.DepartmentService +type UserHandler struct { + *handler.Handler + captchaService v1.CaptchaService + userService v1.UserService + roleService v1.RoleService + departmentService v1.DepartmentService } func NewUserHandler( - render tpl.Renderer, - mi middleware.Middleware, - captchasvc v1.CaptchaService, - usersvc v1.UserService, - rolesvc v1.RoleService, - departmentsvc v1.DepartmentService, -) *userHandler { - return &userHandler{ - render: render, - mi: mi, - captchasvc: captchasvc, - usersvc: usersvc, - rolesvc: rolesvc, - departmentsvc: departmentsvc, + handler *handler.Handler, + captchaService v1.CaptchaService, + userService v1.UserService, + roleService v1.RoleService, + departmentService v1.DepartmentService, +) *UserHandler { + return &UserHandler{ + Handler: handler, + captchaService: captchaService, + userService: userService, + roleService: roleService, + departmentService: departmentService, } } -func (h *userHandler) Add(w http.ResponseWriter, r *http.Request) { - h.render.HTML(w, r, "user/edit.tmpl", map[string]any{ +func (h *UserHandler) Add(w http.ResponseWriter, r *http.Request) { + h.HTML(w, r, "user/edit.tmpl", map[string]any{ "Item": &systemmodel.User{ HashedPassword: nil, }, }) } -func (h *userHandler) Edit(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) Edit(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() id := convertor.QueryInt[int32](vars, "id", 0) user := &systemmodel.User{} if id > 0 { ctx := r.Context() - if u, err := h.usersvc.Get(ctx, id); err == nil { + if u, err := h.userService.Get(ctx, id); err == nil { user.HashedPassword = []byte("********") user = u } } - h.render.HTML(w, r, "user/edit.tmpl", map[string]any{ + h.HTML(w, r, "user/edit.tmpl", map[string]any{ "Item": user, }) } -func (h *userHandler) Save(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) Save(w http.ResponseWriter, r *http.Request) { var req form.User if err := binding.Form.Bind(r, &req); err != nil { - h.render.JSONERR(w, binding.ValidatorErrors(err)) + h.JSONErr(w, binding.ValidatorErrors(err)) return } ctx := r.Context() if req.DepartmentID > 0 { - if _, err := h.departmentsvc.Get(ctx, req.DepartmentID); err != nil { - h.render.JSONERR(w, "部门数据错误") + if _, err := h.departmentService.Get(ctx, req.DepartmentID); err != nil { + h.JSONErr(w, "部门数据错误") return } } if req.RoleID > 0 { - if _, err := h.rolesvc.Get(ctx, req.RoleID); err != nil { - h.render.JSONERR(w, "角色数据错误") + if _, err := h.roleService.Get(ctx, req.RoleID); err != nil { + h.JSONErr(w, "角色数据错误") return } } if *req.ID == 0 { - err := h.usersvc.Create(ctx, &req) + err := h.userService.Create(ctx, &req) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "添加成功") + h.JSONOk(w, "添加成功") } else { - err := h.usersvc.Update(ctx, &req) + err := h.userService.Update(ctx, &req) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "更新成功") + h.JSONOk(w, "更新成功") } } -func (h *userHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - h.render.HTML(w, r, "user/list.tmpl", map[string]any{ + h.HTML(w, r, "user/list.tmpl", map[string]any{ "Statuses": html.NewSelectControls(html.SearchStatuses, "0"), }) case http.MethodPost: @@ -123,89 +119,97 @@ func (h *userHandler) List(w http.ResponseWriter, r *http.Request) { q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0) q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) - res, count, err := h.usersvc.List(r.Context(), q) + res, count, err := h.userService.List(r.Context(), q) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - data := tpl.ResponseList{ + data := render.ResponseList{ Code: 0, Message: "ok", Count: count, Data: res, } - h.render.JSON(w, data) + h.JSON(w, data) default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } } -func (h *userHandler) Profile(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) Profile(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - user := h.mi.AuthUser(ctx) - vm, _ := h.usersvc.Get(ctx, user.ID) - h.render.HTML(w, r, "user/profile.tmpl", map[string]any{ + user := h.AuthUser(ctx) + vm, _ := h.userService.Get(ctx, user.ID) + h.HTML(w, r, "user/profile.tmpl", map[string]any{ "Item": vm, }) } -func (h *userHandler) Data(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) Data(w http.ResponseWriter, r *http.Request) { vars := r.URL.Query() t := vars.Get("type") - if t == "xmselect" { - res, err := h.usersvc.XmSelect(r.Context()) + if t == "xm_select" { + res, err := h.userService.XmSelect(r.Context()) if err != nil { - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSON(w, res) + h.JSON(w, res) return } - h.render.JSON(w, nil) + h.JSON(w, nil) } -func (h *userHandler) Login(w http.ResponseWriter, r *http.Request) { +func (h *UserHandler) Login(w http.ResponseWriter, r *http.Request) { ctx := r.Context() switch r.Method { case http.MethodGet: - if h.mi.IsAuth(ctx) && h.mi.RefreshToken(ctx) { + if h.Middleware.IsAuth(ctx) && h.Middleware.RefreshToken(ctx) { http.Redirect(w, r, "/home.html", http.StatusFound) return } - _ = h.mi.Destroy(ctx) - h.render.HTML(w, r, "oauth/login.tmpl", nil) + _ = h.Middleware.Destroy(ctx) + h.HTML(w, r, "oauth/login.tmpl", nil) case http.MethodPost: - defer r.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + h.Log.Error(err.Error(), err) + } + }(r.Body) var req form.Login if err := binding.Form.Bind(r, &req); err != nil { e := binding.ValidatorErrors(err) - h.render.JSONERR(w, e) + h.JSONErr(w, e) return } - if !h.captchasvc.Verify(req.CaptchaID, req.Captcha, true) { - h.render.JSONERR(w, "验证码错误") + if !h.captchaService.Verify(req.CaptchaID, req.Captcha, true) { + h.JSONErr(w, "验证码错误") return } req = req.SetAttributes(r) - err := h.usersvc.Login(ctx, &req) + err := h.userService.Login(ctx, &req) if err != nil { log.Println(err) - h.render.JSONERR(w, err.Error()) + h.JSONErr(w, err.Error()) return } - h.render.JSONOK(w, "login successfully") + h.JSONOk(w, "login successfully") default: http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) } } -func (h *userHandler) Logout(w http.ResponseWriter, r *http.Request) { - h.mi.Destroy(r.Context()) +func (h *UserHandler) Logout(w http.ResponseWriter, r *http.Request) { + err := h.Middleware.Destroy(r.Context()) + if err != nil { + h.Log.Error(err.Error(), err) + } http.Redirect(w, r, "/", http.StatusFound) } diff --git a/internal/erpserver/handler/router.go b/internal/erpserver/http.go similarity index 53% rename from internal/erpserver/handler/router.go rename to internal/erpserver/http.go index 9d40819..0256b3e 100644 --- a/internal/erpserver/handler/router.go +++ b/internal/erpserver/http.go @@ -1,16 +1,29 @@ -package handler +package erpserver import ( "net/http" "management/internal/erpserver/handler/common" "management/internal/erpserver/handler/system" + mi "management/internal/pkg/middleware" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" ) -func (ctx *AppContext) Router() *chi.Mux { +func NewHTTPServer( + mi mi.Middleware, + captchaHandler *common.CaptchaHandler, + uploadHandler *common.UploadHandler, + configHandler *system.ConfigHandler, + homeHandler *system.HomeHandler, + userHandler *system.UserHandler, + loginLogHandler *system.LoginLogHandler, + auditHandler *system.AuditHandler, + menuHandler *system.MenuHandler, + roleHandler *system.RoleHandler, + departmentHandler *system.DepartmentHandler, +) *chi.Mux { r := chi.NewRouter() r.Use(middleware.RequestID) @@ -25,36 +38,41 @@ func (ctx *AppContext) Router() *chi.Mux { r.Handle("/upload/*", http.StripPrefix("/upload", uploadServer)) r.Group(func(r chi.Router) { - r.Use(ctx.Middleware.NoSurf) // CSRF - r.Use(ctx.Middleware.LoadSession) // Session + r.Use(mi.NoSurf) // CSRF + r.Use(mi.LoadSession) // Session - captchaHandler := common.NewCaptchaHandler(&ctx.Config.Captcha, ctx.Render, ctx.CaptchaService) r.Get("/captcha", captchaHandler.Captcha) - uploadHandler := common.NewUploadHandler(ctx.Render) - r.With(ctx.Middleware.Authorize, ctx.Middleware.Audit).Post("/upload/img", uploadHandler.Img) - r.With(ctx.Middleware.Authorize, ctx.Middleware.Audit).Post("/upload/file", uploadHandler.File) - r.With(ctx.Middleware.Authorize, ctx.Middleware.Audit).Post("/upload/mutilfile", uploadHandler.MutilFiles) + r.With(mi.Authorize, mi.Audit).Post("/upload/img", uploadHandler.Img) + r.With(mi.Authorize, mi.Audit).Post("/upload/file", uploadHandler.File) + r.With(mi.Authorize, mi.Audit).Post("/upload/multi_files", uploadHandler.MultiFiles) - userHandler := system.NewUserHandler(ctx.Render, ctx.Middleware, ctx.CaptchaService, ctx.UserService, ctx.RoleService, ctx.DepartmentService) r.Get("/", userHandler.Login) r.Post("/login", userHandler.Login) r.Get("/logout", userHandler.Logout) - homeHandler := system.NewHomeHandler(ctx.Render, ctx.Middleware, ctx.UserService, ctx.LoginLogService) - r.With(ctx.Middleware.Authorize).Get("/home.html", homeHandler.Home) - r.With(ctx.Middleware.Authorize).Get("/dashboard", homeHandler.Dashboard) + r.With(mi.Authorize).Get("/home.html", homeHandler.Home) + r.With(mi.Authorize).Get("/dashboard", homeHandler.Dashboard) - configHandler := system.NewConfigHandler(ctx.Render, ctx.Redis, ctx.ConfigService) - r.With(ctx.Middleware.Authorize).Get("/pear.json", configHandler.Pear) + r.With(mi.Authorize).Get("/pear.json", configHandler.Pear) r.Route("/system", func(r chi.Router) { - r.Use(ctx.Middleware.Authorize) + r.Use(mi.Authorize) + + r.Route("/config", func(r chi.Router) { + r.Use(mi.Audit) + r.Get("/list", configHandler.List) + r.Post("/list", configHandler.List) + r.Get("/add", configHandler.Add) + r.Get("/edit", configHandler.Edit) + r.Post("/save", configHandler.Save) + r.Post("/refresh_cache", configHandler.RefreshCache) + r.Post("/reset_pear", configHandler.ResetPear) + }) - menuHandler := system.NewMenuHandler(ctx.Render, ctx.Middleware, ctx.MenuService) r.Get("/menus", menuHandler.Menus) r.Route("/menu", func(r chi.Router) { - r.Use(ctx.Middleware.Audit) + r.Use(mi.Audit) r.Get("/list", menuHandler.List) r.Post("/list", menuHandler.List) r.Get("/add", menuHandler.Add) @@ -66,24 +84,22 @@ func (ctx *AppContext) Router() *chi.Mux { }) r.Route("/department", func(r chi.Router) { - r.Use(ctx.Middleware.Audit) + r.Use(mi.Audit) - departHandler := system.NewDepartmentHandler(ctx.Render, ctx.DepartmentService) - r.Get("/list", departHandler.List) - r.Post("/list", departHandler.List) - r.Get("/add", departHandler.Add) - r.Get("/add_children", departHandler.AddChildren) - r.Get("/edit", departHandler.Edit) - r.Post("/save", departHandler.Save) - r.Post("/data", departHandler.Data) - r.Post("/refresh_cache", departHandler.RefreshCache) - r.Post("/rebuild_parent_path", departHandler.RebuildParentPath) + r.Get("/list", departmentHandler.List) + r.Post("/list", departmentHandler.List) + r.Get("/add", departmentHandler.Add) + r.Get("/add_children", departmentHandler.AddChildren) + r.Get("/edit", departmentHandler.Edit) + r.Post("/save", departmentHandler.Save) + r.Post("/data", departmentHandler.Data) + r.Post("/refresh_cache", departmentHandler.RefreshCache) + r.Post("/rebuild_parent_path", departmentHandler.RebuildParentPath) }) r.Route("/role", func(r chi.Router) { - r.Use(ctx.Middleware.Audit) + r.Use(mi.Audit) - roleHandler := system.NewRoleHandler(ctx.Render, ctx.RoleService, ctx.MenuService) r.Get("/list", roleHandler.List) r.Post("/list", roleHandler.List) r.Get("/add", roleHandler.Add) @@ -108,13 +124,11 @@ func (ctx *AppContext) Router() *chi.Mux { r.Post("/data", userHandler.Data) }) - loginLogHandler := system.NewLoginLogHandler(ctx.Render, ctx.LoginLogService) r.Route("/login_log", func(r chi.Router) { r.Get("/list", loginLogHandler.List) r.Post("/list", loginLogHandler.List) }) - auditHandler := system.NewAuditHandler(ctx.Render, ctx.AuditLogService) r.Route("/audit_log", func(r chi.Router) { r.Get("/list", auditHandler.List) r.Post("/list", auditHandler.List) diff --git a/internal/erpserver/model/system/config.go b/internal/erpserver/model/system/config.go index 5f364ca..a112ba9 100644 --- a/internal/erpserver/model/system/config.go +++ b/internal/erpserver/model/system/config.go @@ -18,7 +18,7 @@ type ConfigRepository interface { type Config struct { ID int32 `json:"id" gorm:"primaryKey;autoIncrement;not null"` Key string `json:"key" gorm:"type:varchar(200);not null;uniqueIndex"` - Value []byte `json:"value" gorm:"type:bytea;not null;"` + Value string `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';"` } diff --git a/internal/erpserver/repository/store.go b/internal/erpserver/repository/store.go index 9b0a216..1ac8ee6 100644 --- a/internal/erpserver/repository/store.go +++ b/internal/erpserver/repository/store.go @@ -2,58 +2,136 @@ package repository import ( "context" - "sync" + "fmt" + "time" + "management/internal/pkg/config" + + "github.com/drhin/logger" + "gorm.io/driver/postgres" "gorm.io/gorm" ) -var ( - once sync.Once - // 全局变量,方便其它包直接调用已初始化好的 datastore 实例. - engine *datastore -) +type txCtxKey struct{} -type Store interface { - DB(ctx context.Context) *gorm.DB - TX(ctx context.Context, fn func(ctx context.Context) error) error +type Repository struct { + db *gorm.DB + logger *logger.Logger } -// transactionKey 用于在 context.Context 中存储事务上下文的键. -type transactionKey struct{} - -// datastore 是 Storer 的具体实现. -type datastore struct { - core *gorm.DB +func NewRepository(db *gorm.DB, logger *logger.Logger) *Repository { + return &Repository{ + db: db, + logger: logger, + } } -// 确保 datastore 实现了 Storer 接口. -var _ Store = (*datastore)(nil) +type Transaction interface { + Transaction(ctx context.Context, fn func(ctx context.Context) error) error +} -// NewStore 创建一个 Storer 类型的实例. -func NewStore(db *gorm.DB) *datastore { - // 确保 engine 只被初始化一次 - once.Do(func() { - engine = &datastore{db} +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 + } + } + return r.db.WithContext(ctx) +} + +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) }) - - 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 +func NewDB(log *logger.Logger, config *config.Config) (*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, + ) + db, err := gorm.Open(postgres.New(postgres.Config{ + DSN: dsn, + PreferSimpleProtocol: true, // disables implicit prepared statement usage + }), &gorm.Config{}) + if err != nil { + return nil, nil, err } - return db + db = db.Debug() + + // Connection Pool config + sqlDB, err := db.DB() + if err != nil { + return nil, nil, err + } + + sqlDB.SetMaxIdleConns(10) + sqlDB.SetMaxOpenConns(100) + sqlDB.SetConnMaxLifetime(time.Hour) + + cleanup := func() { + if err := sqlDB.Close(); err != nil { + log.Error("sql db close error", err) + } + } + + return db, cleanup, nil } -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) - }, - ) -} +//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) +// }, +// ) +//} diff --git a/internal/erpserver/repository/system/audit_log.go b/internal/erpserver/repository/system/audit_log.go index 9d336fe..8ce3157 100644 --- a/internal/erpserver/repository/system/audit_log.go +++ b/internal/erpserver/repository/system/audit_log.go @@ -9,23 +9,21 @@ import ( ) type auditLogRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.AuditLogRepository = (*auditLogRepository)(nil) - -func NewAuditLogRepository(store repository.Store) *auditLogRepository { +func NewAuditLogRepository(repo *repository.Repository) system.AuditLogRepository { return &auditLogRepository{ - store: store, + repo: repo, } } func (s *auditLogRepository) Create(ctx context.Context, obj *system.AuditLog) error { - return s.store.DB(ctx).Create(obj).Error + return s.repo.DB(ctx).Create(obj).Error } func (s *auditLogRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.AuditLog, int64, error) { - query := s.store.DB(ctx). + query := s.repo.DB(ctx). Model(&system.AuditLog{}). Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd) if q.SearchEmail != "" { diff --git a/internal/erpserver/repository/system/config.go b/internal/erpserver/repository/system/config.go index 026ad8a..96985d3 100644 --- a/internal/erpserver/repository/system/config.go +++ b/internal/erpserver/repository/system/config.go @@ -9,28 +9,26 @@ import ( ) type configRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.ConfigRepository = (*configRepository)(nil) - -func NewConfigRepository(store repository.Store) *configRepository { +func NewConfigRepository(repo *repository.Repository) system.ConfigRepository { return &configRepository{ - store: store, + repo: repo, } } func (r *configRepository) Create(ctx context.Context, obj *system.Config) error { - return r.store.DB(ctx).Create(obj).Error + return r.repo.DB(ctx).Create(obj).Error } func (r *configRepository) Update(ctx context.Context, obj *system.Config) error { - return r.store.DB(ctx).Save(obj).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.store.DB(ctx).First(&obj, id).Error + err := r.repo.DB(ctx).First(&obj, id).Error if err != nil { return nil, err } @@ -39,7 +37,7 @@ func (r *configRepository) Get(ctx context.Context, id int32) (*system.Config, e func (r *configRepository) GetByKey(ctx context.Context, key string) (*system.Config, error) { var obj system.Config - err := r.store.DB(ctx).Where("key = ?", key).First(&obj).Error + err := r.repo.DB(ctx).Where("key = ?", key).First(&obj).Error if err != nil { return nil, err } @@ -47,5 +45,25 @@ func (r *configRepository) GetByKey(ctx context.Context, key string) (*system.Co } func (r *configRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.Config, int64, error) { - return nil, 0, nil + 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 } diff --git a/internal/erpserver/repository/system/department.go b/internal/erpserver/repository/system/department.go index f8003f5..55f5a26 100644 --- a/internal/erpserver/repository/system/department.go +++ b/internal/erpserver/repository/system/department.go @@ -9,28 +9,26 @@ import ( ) type departmentRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.DepartmentRepository = (*departmentRepository)(nil) - -func NewDepartmentRepository(store repository.Store) *departmentRepository { +func NewDepartmentRepository(repo *repository.Repository) system.DepartmentRepository { return &departmentRepository{ - store: store, + repo: repo, } } func (r *departmentRepository) Create(ctx context.Context, obj *system.Department) error { - return r.store.DB(ctx).Create(obj).Error + return r.repo.DB(ctx).Create(obj).Error } func (r *departmentRepository) Update(ctx context.Context, obj *system.Department) error { - return r.store.DB(ctx).Save(obj).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.store.DB(ctx).First(&obj, id).Error + err := r.repo.DB(ctx).First(&obj, id).Error if err != nil { return nil, err } @@ -39,7 +37,7 @@ func (r *departmentRepository) Get(ctx context.Context, id int32) (*system.Depar func (r *departmentRepository) All(ctx context.Context) ([]*system.Department, error) { var departs []*system.Department - err := r.store.DB(ctx).Find(&departs).Error + err := r.repo.DB(ctx).Find(&departs).Error if err != nil { return nil, err } @@ -47,7 +45,7 @@ func (r *departmentRepository) All(ctx context.Context) ([]*system.Department, e } func (r *departmentRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.Department, int64, error) { - query := r.store.DB(ctx). + query := r.repo.DB(ctx). Model(&system.Department{}). Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd) if q.SearchID != 0 { @@ -96,5 +94,5 @@ SET parent_path = (SELECT ',' || string_agg(cast(t.parent_id AS VARCHAR), ',') | FROM temp ORDER BY id) AS t) WHERE tm.status = 0;` - return r.store.DB(ctx).Exec(query).Error + return r.repo.DB(ctx).Exec(query).Error } diff --git a/internal/erpserver/repository/system/login_log.go b/internal/erpserver/repository/system/login_log.go index d7c0d3a..d5174d9 100644 --- a/internal/erpserver/repository/system/login_log.go +++ b/internal/erpserver/repository/system/login_log.go @@ -9,24 +9,22 @@ import ( ) type loginLogRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.LoginLogRepository = (*loginLogRepository)(nil) - -func NewLoginLogRepository(store repository.Store) *loginLogRepository { +func NewLoginLogRepository(repo *repository.Repository) system.LoginLogRepository { return &loginLogRepository{ - store: store, + repo: repo, } } func (s *loginLogRepository) Create(ctx context.Context, obj *system.LoginLog) error { - return s.store.DB(ctx).Create(obj).Error + return s.repo.DB(ctx).Create(obj).Error } func (s *loginLogRepository) GetLatest(ctx context.Context, email string) (*system.LoginLog, error) { var log system.LoginLog - err := s.store.DB(ctx). + err := s.repo.DB(ctx). Where("email = ?", email). Order("id DESC"). First(&log). @@ -38,7 +36,7 @@ func (s *loginLogRepository) GetLatest(ctx context.Context, email string) (*syst } func (s *loginLogRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.LoginLog, int64, error) { - query := s.store.DB(ctx). + query := s.repo.DB(ctx). Model(&system.LoginLog{}). Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd) if q.SearchEmail != "" { @@ -66,7 +64,7 @@ func (s *loginLogRepository) List(ctx context.Context, q dto.SearchDto) ([]*syst func (s *loginLogRepository) Count(ctx context.Context, email string) (int64, error) { var count int64 - err := s.store.DB(ctx). + err := s.repo.DB(ctx). Model(&system.LoginLog{}). Where("email = ?", email). Count(&count). diff --git a/internal/erpserver/repository/system/menu.go b/internal/erpserver/repository/system/menu.go index 3076711..f90cb8f 100644 --- a/internal/erpserver/repository/system/menu.go +++ b/internal/erpserver/repository/system/menu.go @@ -8,28 +8,26 @@ import ( ) type menuRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.MenuRepository = (*menuRepository)(nil) - -func NewMenuRepository(store repository.Store) *menuRepository { +func NewMenuRepository(repo *repository.Repository) system.MenuRepository { return &menuRepository{ - store: store, + repo: repo, } } func (r *menuRepository) Create(ctx context.Context, obj *system.Menu) error { - return r.store.DB(ctx).Create(obj).Error + return r.repo.DB(ctx).Create(obj).Error } func (r *menuRepository) Update(ctx context.Context, obj *system.Menu) error { - return r.store.DB(ctx).Save(obj).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.store.DB(ctx).Where("id = ?", id).First(&menu).Error + err := r.repo.DB(ctx).Where("id = ?", id).First(&menu).Error if err != nil { return nil, err } @@ -38,7 +36,7 @@ func (r *menuRepository) Get(ctx context.Context, id int32) (*system.Menu, error func (r *menuRepository) GetByUrl(ctx context.Context, url string) (*system.Menu, error) { var menu system.Menu - err := r.store.DB(ctx).Where("url = ?", url).First(&menu).Error + err := r.repo.DB(ctx).Where("url = ?", url).First(&menu).Error if err != nil { return nil, err } @@ -47,7 +45,7 @@ func (r *menuRepository) GetByUrl(ctx context.Context, url string) (*system.Menu func (r *menuRepository) All(ctx context.Context) ([]*system.Menu, error) { var menus []*system.Menu - err := r.store.DB(ctx).Find(&menus).Error + err := r.repo.DB(ctx).Find(&menus).Error if err != nil { return nil, err } @@ -69,5 +67,5 @@ func (r *menuRepository) RebuildParentPath(ctx context.Context) error { FROM temp ORDER BY id) AS t) WHERE tm.status = 0;` - return r.store.DB(ctx).Exec(query).Error + return r.repo.DB(ctx).Exec(query).Error } diff --git a/internal/erpserver/repository/system/role.go b/internal/erpserver/repository/system/role.go index 22c3b8a..f6f0436 100644 --- a/internal/erpserver/repository/system/role.go +++ b/internal/erpserver/repository/system/role.go @@ -9,28 +9,26 @@ import ( ) type roleRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.RoleRepository = (*roleRepository)(nil) - -func NewRoleRepository(store repository.Store) *roleRepository { +func NewRoleRepository(repo *repository.Repository) system.RoleRepository { return &roleRepository{ - store: store, + repo: repo, } } func (r *roleRepository) Create(ctx context.Context, obj *system.Role) error { - return r.store.DB(ctx).Create(obj).Error + return r.repo.DB(ctx).Create(obj).Error } func (r *roleRepository) Update(ctx context.Context, obj *system.Role) error { - return r.store.DB(ctx).Save(obj).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.store.DB(ctx).Where("id = ?", id).First(&role).Error + err := r.repo.DB(ctx).Where("id = ?", id).First(&role).Error if err != nil { return nil, err } @@ -39,7 +37,7 @@ func (r *roleRepository) Get(ctx context.Context, id int32) (*system.Role, error func (r *roleRepository) All(ctx context.Context) ([]*system.Role, error) { var roles []*system.Role - err := r.store.DB(ctx).Find(&roles).Error + err := r.repo.DB(ctx).Find(&roles).Error if err != nil { return nil, err } @@ -47,7 +45,7 @@ func (r *roleRepository) All(ctx context.Context) ([]*system.Role, error) { } func (r *roleRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.Role, int64, error) { - query := r.store.DB(ctx). + query := r.repo.DB(ctx). Model(&system.Role{}). Where("created_at BETWEEN ? AND ?", q.SearchTimeBegin, q.SearchTimeEnd) if q.SearchID != 0 { @@ -96,5 +94,5 @@ func (r *roleRepository) RebuildParentPath(ctx context.Context) error { FROM temp ORDER BY id) AS t) WHERE tm.status = 0;` - return r.store.DB(ctx).Exec(query).Error + return r.repo.DB(ctx).Exec(query).Error } diff --git a/internal/erpserver/repository/system/role_menu.go b/internal/erpserver/repository/system/role_menu.go index 21e6121..d49865d 100644 --- a/internal/erpserver/repository/system/role_menu.go +++ b/internal/erpserver/repository/system/role_menu.go @@ -8,28 +8,26 @@ import ( ) type roleMenuRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.RoleMenuRepository = (*roleMenuRepository)(nil) - -func NewRoleMenuRepository(store repository.Store) *roleMenuRepository { +func NewRoleMenuRepository(repo *repository.Repository) system.RoleMenuRepository { return &roleMenuRepository{ - store: store, + repo: repo, } } func (r *roleMenuRepository) Create(ctx context.Context, obj []*system.RoleMenu) error { - return r.store.DB(ctx).Create(obj).Error + return r.repo.DB(ctx).Create(obj).Error } func (r *roleMenuRepository) DeleteByRoleID(ctx context.Context, roleID int32) error { - return r.store.DB(ctx).Where("role_id = ?", roleID).Delete(&system.RoleMenu{}).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.store.DB(ctx).Where("role_id = ?", roleID).Find(&roleMenus).Error + err := r.repo.DB(ctx).Where("role_id = ?", roleID).Find(&roleMenus).Error if err != nil { return nil, err } diff --git a/internal/erpserver/repository/system/user.go b/internal/erpserver/repository/system/user.go index 5d29407..7d57f53 100644 --- a/internal/erpserver/repository/system/user.go +++ b/internal/erpserver/repository/system/user.go @@ -9,28 +9,26 @@ import ( ) type userRepository struct { - store repository.Store + repo *repository.Repository } -var _ system.UserRepository = (*userRepository)(nil) - -func NewUserRepository(store repository.Store) *userRepository { +func NewUserRepository(repo *repository.Repository) system.UserRepository { return &userRepository{ - store: store, + repo: repo, } } func (s *userRepository) Create(ctx context.Context, obj *system.User) error { - return s.store.DB(ctx).Create(obj).Error + return s.repo.DB(ctx).Create(obj).Error } func (s *userRepository) Update(ctx context.Context, obj *system.User) error { - return s.store.DB(ctx).Save(obj).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.store.DB(ctx).Where("id = ?", id).First(&user).Error + err := s.repo.DB(ctx).Where("id = ?", id).First(&user).Error if err != nil { return nil, err } @@ -39,7 +37,7 @@ func (s *userRepository) Get(ctx context.Context, id int32) (*system.User, error func (s *userRepository) GetByEmail(ctx context.Context, email string) (*system.User, error) { var user system.User - err := s.store.DB(ctx).Where("email = ?", email).First(&user).Error + err := s.repo.DB(ctx).Where("email = ?", email).First(&user).Error if err != nil { return nil, err } @@ -48,7 +46,7 @@ func (s *userRepository) GetByEmail(ctx context.Context, email string) (*system. func (s *userRepository) All(ctx context.Context) ([]*system.User, error) { var users []*system.User - err := s.store.DB(ctx).Find(&users).Error + err := s.repo.DB(ctx).Find(&users).Error if err != nil { return nil, err } @@ -56,7 +54,7 @@ func (s *userRepository) All(ctx context.Context) ([]*system.User, error) { } func (s *userRepository) List(ctx context.Context, q dto.SearchDto) ([]*system.User, int64, error) { - query := s.store.DB(ctx). + query := s.repo.DB(ctx). Model(&system.User{}). Preload("Role"). Preload("Department"). diff --git a/internal/erpserver/service/service.go b/internal/erpserver/service/service.go new file mode 100644 index 0000000..d0f0f74 --- /dev/null +++ b/internal/erpserver/service/service.go @@ -0,0 +1,30 @@ +package service + +import ( + "management/internal/erpserver/repository" + "management/internal/pkg/redis" + "management/internal/pkg/session" + + "github.com/drhin/logger" +) + +type Service struct { + Log *logger.Logger + Tx repository.Transaction + Session session.Session + Redis redis.Cache +} + +func NewService( + log *logger.Logger, + tx repository.Transaction, + session session.Session, + redis redis.Cache, +) *Service { + return &Service{ + Log: log, + Tx: tx, + Session: session, + Redis: redis, + } +} diff --git a/internal/erpserver/service/v1/common/captcha.go b/internal/erpserver/service/v1/common/captcha.go index 0a125e6..302b955 100644 --- a/internal/erpserver/service/v1/common/captcha.go +++ b/internal/erpserver/service/v1/common/captcha.go @@ -8,13 +8,11 @@ import ( type captchaService struct{} -var _ v1.CaptchaService = (*captchaService)(nil) - -func NewCaptchaService() *captchaService { +func NewCaptchaService() v1.CaptchaService { return &captchaService{} } -var captchaStore base64Captcha.Store = base64Captcha.DefaultMemStore +var captchaStore = base64Captcha.DefaultMemStore func (b *captchaService) Generate(height int, width int, length int, maxSkew float64, dotCount int) (id, b64s, answer string, err error) { driver := base64Captcha.NewDriverDigit(height, width, length, maxSkew, dotCount) diff --git a/internal/erpserver/service/v1/service.go b/internal/erpserver/service/v1/service.go index 792ec04..26db284 100644 --- a/internal/erpserver/service/v1/service.go +++ b/internal/erpserver/service/v1/service.go @@ -21,6 +21,9 @@ type ConfigService interface { Get(ctx context.Context, id int32) (*system.Config, error) List(ctx context.Context, q dto.SearchDto) ([]*system.Config, int64, error) Pear(ctx context.Context) (*dto.PearConfig, error) + + RefreshCache(ctx context.Context, key string) error + ResetPear(ctx context.Context) error } type UserService interface { diff --git a/internal/erpserver/service/v1/system/audit_log.go b/internal/erpserver/service/v1/system/audit_log.go index 5f35193..b79c7a4 100644 --- a/internal/erpserver/service/v1/system/audit_log.go +++ b/internal/erpserver/service/v1/system/audit_log.go @@ -5,18 +5,19 @@ import ( "management/internal/erpserver/model/dto" "management/internal/erpserver/model/system" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" ) type auditLogService struct { + *service.Service repo system.AuditLogRepository } -var _ v1.AuditLogService = (*auditLogService)(nil) - -func NewAuditLogService(repo system.AuditLogRepository) *auditLogService { +func NewAuditLogService(service *service.Service, repo system.AuditLogRepository) v1.AuditLogService { return &auditLogService{ - repo: repo, + Service: service, + repo: repo, } } diff --git a/internal/erpserver/service/v1/system/config.go b/internal/erpserver/service/v1/system/config.go index 0f604ce..0fa6875 100644 --- a/internal/erpserver/service/v1/system/config.go +++ b/internal/erpserver/service/v1/system/config.go @@ -3,27 +3,28 @@ package system import ( "context" "encoding/json" + "errors" "time" "management/internal/erpserver/model/dto" "management/internal/erpserver/model/system" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/know" "management/internal/pkg/know/pearadmin" - "management/internal/pkg/redis" + + "gorm.io/gorm" ) type configService struct { - repo system.ConfigRepository - redis redis.RedisCache + *service.Service + repo system.ConfigRepository } -var _ v1.ConfigService = (*configService)(nil) - -func NewConfigService(repo system.ConfigRepository, redis redis.RedisCache) *configService { +func NewConfigService(service *service.Service, repo system.ConfigRepository) v1.ConfigService { return &configService{ - repo: repo, - redis: redis, + Service: service, + repo: repo, } } @@ -46,7 +47,7 @@ func (s *configService) List(ctx context.Context, q dto.SearchDto) ([]*system.Co func (s *configService) Pear(ctx context.Context) (*dto.PearConfig, error) { // 判断redis是否存储 key := know.GetManageKey(ctx, know.PearAdmin) - bs, err := s.redis.GetBytes(ctx, key) + bs, err := s.Redis.GetBytes(ctx, key) if err == nil { var res *dto.PearConfig if err := json.Unmarshal(bs, &res); err == nil { @@ -60,10 +61,48 @@ func (s *configService) Pear(ctx context.Context) (*dto.PearConfig, error) { } var pear dto.PearConfig - if err := json.Unmarshal(conf.Value, &pear); err != nil { + if err := json.Unmarshal([]byte(conf.Value), &pear); err != nil { return nil, err } - _ = s.redis.Set(ctx, key, conf.Value, time.Hour*6) + _ = s.Redis.Set(ctx, key, conf.Value, time.Hour*6) return &pear, nil } + +func (s *configService) RefreshCache(ctx context.Context, key string) error { + return s.Redis.Del(ctx, key) +} + +func (s *configService) ResetPear(ctx context.Context) error { + b, err := json.Marshal(pearadmin.PearJson) + if err != nil { + return err + } + + conf, err := s.repo.GetByKey(ctx, pearadmin.PearKey) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + if errors.Is(err, gorm.ErrRecordNotFound) { + // create + conf = &system.Config{ + Key: pearadmin.PearKey, + Value: string(b), + } + err = s.Create(ctx, conf) + if err != nil { + return err + } + } else { + // update + conf.Value = string(b) + conf.UpdatedAt = time.Now() + err = s.Update(ctx, conf) + if err != nil { + return err + } + } + + return nil +} diff --git a/internal/erpserver/service/v1/system/department.go b/internal/erpserver/service/v1/system/department.go index 57f458d..4b40020 100644 --- a/internal/erpserver/service/v1/system/department.go +++ b/internal/erpserver/service/v1/system/department.go @@ -12,22 +12,22 @@ import ( "management/internal/erpserver/model/form" "management/internal/erpserver/model/system" "management/internal/erpserver/model/view" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/convertor" "management/internal/pkg/database" "management/internal/pkg/know" - "management/internal/pkg/redis" ) type departmentService struct { + *service.Service repo system.DepartmentRepository } -var _ v1.DepartmentService = (*departmentService)(nil) - -func NewDepartmentService(repo system.DepartmentRepository) *departmentService { +func NewDepartmentService(service *service.Service, repo system.DepartmentRepository) v1.DepartmentService { return &departmentService{ - repo: repo, + Service: service, + repo: repo, } } @@ -108,7 +108,7 @@ func (s *departmentService) Get(ctx context.Context, id int32) (*system.Departme func (s *departmentService) All(ctx context.Context) ([]*system.Department, error) { key := know.GetManageKey(ctx, know.AllDepartments) - bs, err := redis.GetBytes(ctx, key) + bs, err := s.Redis.GetBytes(ctx, key) if err == nil { var res []*system.Department if err := json.Unmarshal(bs, &res); err == nil { @@ -126,7 +126,7 @@ func (s *departmentService) All(ctx context.Context) ([]*system.Department, erro return nil, err } - _ = redis.Set(ctx, key, bs, time.Hour*6) + _ = s.Redis.Set(ctx, key, bs, time.Hour*6) return res, nil } @@ -146,7 +146,7 @@ func (s *departmentService) RefreshCache(ctx context.Context) error { return err } - _ = redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return nil } diff --git a/internal/erpserver/service/v1/system/login_log.go b/internal/erpserver/service/v1/system/login_log.go index c5863b4..a9e3c99 100644 --- a/internal/erpserver/service/v1/system/login_log.go +++ b/internal/erpserver/service/v1/system/login_log.go @@ -6,18 +6,19 @@ import ( "management/internal/erpserver/model/dto" "management/internal/erpserver/model/system" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" ) type loginLogService struct { + *service.Service repo system.LoginLogRepository } -var _ v1.LoginLogService = (*loginLogService)(nil) - -func NewLoginLogService(repo system.LoginLogRepository) *loginLogService { +func NewLoginLogService(service *service.Service, repo system.LoginLogRepository) v1.LoginLogService { return &loginLogService{ - repo: repo, + Service: service, + repo: repo, } } diff --git a/internal/erpserver/service/v1/system/menu.go b/internal/erpserver/service/v1/system/menu.go index 85a38f4..f146f1c 100644 --- a/internal/erpserver/service/v1/system/menu.go +++ b/internal/erpserver/service/v1/system/menu.go @@ -10,29 +10,29 @@ import ( "management/internal/erpserver/model/dto" "management/internal/erpserver/model/system" "management/internal/erpserver/model/view" - "management/internal/erpserver/repository" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/know" - "management/internal/pkg/redis" ) type menuService struct { - redis redis.RedisCache - store repository.Store - repo system.MenuRepository - rolesvc v1.RoleService - rolemenusvc v1.RoleMenuService + *service.Service + repo system.MenuRepository + roleService v1.RoleService + roleMenuService v1.RoleMenuService } -var _ v1.MenuService = (*menuService)(nil) - -func NewMenuService(redis redis.RedisCache, store repository.Store, repo system.MenuRepository, rolesvc v1.RoleService, rolemenusvc v1.RoleMenuService) *menuService { +func NewMenuService( + service *service.Service, + repo system.MenuRepository, + roleService v1.RoleService, + roleMenuService v1.RoleMenuService, +) v1.MenuService { return &menuService{ - redis: redis, - store: store, - repo: repo, - rolesvc: rolesvc, - rolemenusvc: rolemenusvc, + Service: service, + repo: repo, + roleService: roleService, + roleMenuService: roleMenuService, } } @@ -54,7 +54,7 @@ func (s *menuService) GetByUrl(ctx context.Context, url string) (*system.Menu, e func (s *menuService) All(ctx context.Context) ([]*system.Menu, error) { key := know.GetManageKey(ctx, know.AllMenus) - b, err := s.redis.GetBytes(ctx, key) + b, err := s.Redis.GetBytes(ctx, key) if err == nil { var res []*system.Menu if err := json.Unmarshal(b, &res); err == nil { @@ -72,7 +72,7 @@ func (s *menuService) All(ctx context.Context) ([]*system.Menu, error) { return nil, err } - _ = s.redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return res, nil } @@ -87,7 +87,7 @@ func (s *menuService) ListMenuTree(ctx context.Context) ([]*view.MenuTree, error func (s *menuService) ListByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error) { key := know.GetManageKey(ctx, know.OwnerMenus, roleID) - bs, err := s.redis.GetBytes(ctx, key) + bs, err := s.Redis.GetBytes(ctx, key) if err == nil { var res []*dto.OwnerMenuDto if err := json.Unmarshal(bs, &res); err == nil { @@ -111,13 +111,13 @@ func (s *menuService) SetListByRoleID(ctx context.Context, roleID int32) ([]*dto return nil, err } - _ = s.redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return res, nil } func (s *menuService) ListByRoleIDToMap(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) { key := know.GetManageKey(ctx, know.OwnerMenusMap, roleID) - bs, err := s.redis.GetBytes(ctx, key) + bs, err := s.Redis.GetBytes(ctx, key) if err == nil { var res map[string]*dto.OwnerMenuDto if err := json.Unmarshal(bs, &res); err == nil { @@ -141,13 +141,13 @@ func (s *menuService) SetListByRoleIDToMap(ctx context.Context, roleID int32) (m return nil, err } - _ = s.redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return res, nil } func (s *menuService) OwerMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error) { key := know.GetManageKey(ctx, know.AdminMenus, roleID) - bs, err := s.redis.GetBytes(ctx, key) + bs, err := s.Redis.GetBytes(ctx, key) if err == nil { var res []*dto.MenuUIDto if err := json.Unmarshal(bs, &res); err == nil { @@ -172,7 +172,7 @@ func (s *menuService) SetOwerMenus(ctx context.Context, roleID int32) ([]*dto.Me return nil, err } - _ = s.redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return res, nil } @@ -193,15 +193,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.store.TX(ctx, func(ctx context.Context) error { + return s.Tx.Transaction(ctx, func(ctx context.Context) error { // 先删除该角色的所有权限 - err := s.rolemenusvc.DeleteByRoleID(ctx, roleID) + err := s.roleMenuService.DeleteByRoleID(ctx, roleID) if err != nil { return err } // 再添加该角色的所有权限 - return s.rolemenusvc.Create(ctx, rms) + return s.roleMenuService.Create(ctx, rms) }) } @@ -217,7 +217,7 @@ func (s *menuService) RefreshCache(ctx context.Context) error { return err } - _ = redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return nil } @@ -245,7 +245,7 @@ func (s *menuService) XmSelectTree(ctx context.Context, id int32) ([]*view.XmSel // listByRoleID 获取该角色所拥有的菜单集合 func (s *menuService) listByRoleID(ctx context.Context, roleID int32) ([]*system.Menu, error) { - role, err := s.rolesvc.Get(ctx, roleID) + role, err := s.roleService.Get(ctx, roleID) if err != nil { return nil, err } @@ -260,7 +260,7 @@ func (s *menuService) listByRoleID(ctx context.Context, roleID int32) ([]*system } // 通过角色获取所拥有的权限 - roleMenus, err := s.rolemenusvc.ListByRoleID(ctx, roleID) + roleMenus, err := s.roleMenuService.ListByRoleID(ctx, roleID) if err != nil { return nil, err } diff --git a/internal/erpserver/service/v1/system/role.go b/internal/erpserver/service/v1/system/role.go index 1364935..9ad87f8 100644 --- a/internal/erpserver/service/v1/system/role.go +++ b/internal/erpserver/service/v1/system/role.go @@ -12,22 +12,22 @@ import ( "management/internal/erpserver/model/form" "management/internal/erpserver/model/system" "management/internal/erpserver/model/view" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/convertor" "management/internal/pkg/database" "management/internal/pkg/know" - "management/internal/pkg/redis" ) type roleService struct { + *service.Service repo system.RoleRepository } -var _ v1.RoleService = (*roleService)(nil) - -func NewRoleService(repo system.RoleRepository) *roleService { +func NewRoleService(service *service.Service, repo system.RoleRepository) v1.RoleService { return &roleService{ - repo: repo, + Service: service, + repo: repo, } } @@ -111,7 +111,7 @@ func (s *roleService) Get(ctx context.Context, id int32) (*system.Role, error) { func (s *roleService) All(ctx context.Context) ([]*system.Role, error) { key := know.GetManageKey(ctx, know.AllRoles) - bs, err := redis.GetBytes(ctx, key) + bs, err := s.Redis.GetBytes(ctx, key) if err == nil { var res []*system.Role if err := json.Unmarshal(bs, &res); err == nil { @@ -129,7 +129,7 @@ func (s *roleService) All(ctx context.Context) ([]*system.Role, error) { return nil, err } - _ = redis.Set(ctx, key, bs, time.Hour*6) + _ = s.Redis.Set(ctx, key, bs, time.Hour*6) return res, nil } @@ -149,7 +149,7 @@ func (s *roleService) RefreshCache(ctx context.Context) error { return err } - _ = redis.Set(ctx, key, b, time.Hour*6) + _ = s.Redis.Set(ctx, key, b, time.Hour*6) return nil } diff --git a/internal/erpserver/service/v1/system/role_menu.go b/internal/erpserver/service/v1/system/role_menu.go index cf1b800..ac48a2e 100644 --- a/internal/erpserver/service/v1/system/role_menu.go +++ b/internal/erpserver/service/v1/system/role_menu.go @@ -4,18 +4,19 @@ import ( "context" "management/internal/erpserver/model/system" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" ) type roleMenuService struct { + *service.Service repo system.RoleMenuRepository } -var _ v1.RoleMenuService = (*roleMenuService)(nil) - -func NewRoleMenuService(repo system.RoleMenuRepository) *roleMenuService { +func NewRoleMenuService(service *service.Service, repo system.RoleMenuRepository) v1.RoleMenuService { return &roleMenuService{ - repo: repo, + Service: service, + repo: repo, } } diff --git a/internal/erpserver/service/v1/system/user.go b/internal/erpserver/service/v1/system/user.go index 80937bd..bd32081 100644 --- a/internal/erpserver/service/v1/system/user.go +++ b/internal/erpserver/service/v1/system/user.go @@ -11,39 +11,39 @@ import ( "management/internal/erpserver/model/form" "management/internal/erpserver/model/system" "management/internal/erpserver/model/view" + "management/internal/erpserver/service" v1 "management/internal/erpserver/service/v1" "management/internal/pkg/crypto" "management/internal/pkg/database" "management/internal/pkg/know" "management/internal/pkg/rand" - "management/internal/pkg/session" - "github.com/drhin/logger" "github.com/google/uuid" "go.uber.org/zap" ) type userService struct { - session session.Session - log *logger.Logger + *service.Service repo system.UserRepository roleService v1.RoleService loginLogService v1.LoginLogService } -var _ v1.UserService = (*userService)(nil) - -func NewUserService(session session.Session, log *logger.Logger, repo system.UserRepository, roleService v1.RoleService, loginLogService v1.LoginLogService) *userService { +func NewUserService( + service *service.Service, + repo system.UserRepository, + roleService v1.RoleService, + loginLogService v1.LoginLogService, +) v1.UserService { return &userService{ - session: session, - log: log, + Service: service, repo: repo, roleService: roleService, loginLogService: loginLogService, } } -func (b *userService) Create(ctx context.Context, req *form.User) error { +func (s *userService) Create(ctx context.Context, req *form.User) error { salt, err := rand.String(10) if err != nil { return err @@ -74,7 +74,7 @@ func (b *userService) Create(ctx context.Context, req *form.User) error { CreatedAt: time.Now(), UpdatedAt: time.Now(), } - err = b.repo.Create(ctx, user) + err = s.repo.Create(ctx, user) if err != nil { if database.IsUniqueViolation(err) { return errors.New("用户已经存在") @@ -84,8 +84,8 @@ func (b *userService) Create(ctx context.Context, req *form.User) error { return nil } -func (b *userService) Update(ctx context.Context, req *form.User) error { - user, err := b.repo.Get(ctx, *req.ID) +func (s *userService) Update(ctx context.Context, req *form.User) error { + user, err := s.repo.Get(ctx, *req.ID) if err != nil { return err } @@ -105,23 +105,23 @@ func (b *userService) Update(ctx context.Context, req *form.User) error { user.HashedPassword = hashedPassword user.ChangePasswordAt = time.Now() } - return b.repo.Update(ctx, user) + return s.repo.Update(ctx, user) } -func (b *userService) All(ctx context.Context) ([]*system.User, error) { - return b.repo.All(ctx) +func (s *userService) All(ctx context.Context) ([]*system.User, error) { + return s.repo.All(ctx) } -func (b *userService) List(ctx context.Context, q dto.SearchDto) ([]*system.User, int64, error) { - return b.repo.List(ctx, q) +func (s *userService) List(ctx context.Context, q dto.SearchDto) ([]*system.User, int64, error) { + return s.repo.List(ctx, q) } -func (b *userService) Get(ctx context.Context, id int32) (*system.User, error) { - return b.repo.Get(ctx, id) +func (s *userService) Get(ctx context.Context, id int32) (*system.User, error) { + return s.repo.Get(ctx, id) } -func (b *userService) XmSelect(ctx context.Context) ([]*view.XmSelect, error) { - all, err := b.repo.All(ctx) +func (s *userService) XmSelect(ctx context.Context) ([]*view.XmSelect, error) { + all, err := s.repo.All(ctx) if err != nil || len(all) == 0 { return nil, err } @@ -136,24 +136,24 @@ func (b *userService) XmSelect(ctx context.Context) ([]*view.XmSelect, error) { return res, nil } -func (b *userService) Login(ctx context.Context, req *form.Login) error { +func (s *userService) Login(ctx context.Context, req *form.Login) error { l := system.NewLoginLog(req.Email, req.Os, req.Ip, req.Browser, req.Url, req.Referrer) - err := b.login(ctx, req) + err := s.login(ctx, req) if err != nil { - if err := b.loginLogService.Create(ctx, l.SetMessage(err.Error())); err != nil { - b.log.Error(err.Error(), err, zap.Any("login_log", l)) + if err := s.loginLogService.Create(ctx, l.SetMessage(err.Error())); err != nil { + s.Log.Error(err.Error(), err, zap.Any("login_log", l)) } return err } - if err := b.loginLogService.Create(ctx, l.SetOk("登录成功")); err != nil { - b.log.Error(err.Error(), err, zap.Any("login_log", l)) + if err := s.loginLogService.Create(ctx, l.SetOk("登录成功")); err != nil { + s.Log.Error(err.Error(), err, zap.Any("login_log", l)) } return nil } -func (b *userService) login(ctx context.Context, req *form.Login) error { - user, err := b.repo.GetByEmail(ctx, req.Email) +func (s *userService) login(ctx context.Context, req *form.Login) error { + user, err := s.repo.GetByEmail(ctx, req.Email) if err != nil { return err } @@ -163,7 +163,7 @@ func (b *userService) login(ctx context.Context, req *form.Login) error { return errors.New("账号或密码错误") } - user.Role, err = b.roleService.Get(ctx, user.RoleID) + user.Role, err = s.roleService.Get(ctx, user.RoleID) if err != nil { return err } @@ -172,7 +172,7 @@ func (b *userService) login(ctx context.Context, req *form.Login) error { } // 登陆成功 - err = b.loginSuccess(ctx, user, req) + err = s.loginSuccess(ctx, user, req) if err != nil { return err } @@ -180,7 +180,7 @@ func (b *userService) login(ctx context.Context, req *form.Login) error { return nil } -func (b *userService) loginSuccess(ctx context.Context, user *system.User, req *form.Login) error { +func (s *userService) loginSuccess(ctx context.Context, user *system.User, req *form.Login) error { auth := dto.AuthorizeUser{ ID: user.ID, Uuid: user.Uuid, @@ -198,6 +198,6 @@ func (b *userService) loginSuccess(ctx context.Context, user *system.User, req * return err } - b.session.Put(ctx, know.StoreName, gob) + s.Session.Put(ctx, know.StoreName, gob) return nil } diff --git a/internal/erpserver/wire.go b/internal/erpserver/wire.go new file mode 100644 index 0000000..0025131 --- /dev/null +++ b/internal/erpserver/wire.go @@ -0,0 +1,82 @@ +//go:build wireinject +// +build wireinject + +package erpserver + +import ( + "management/internal/erpserver/handler" + commonHandler "management/internal/erpserver/handler/common" + systemHandler "management/internal/erpserver/handler/system" + "management/internal/erpserver/repository" + systemRepo "management/internal/erpserver/repository/system" + "management/internal/erpserver/service" + commonService "management/internal/erpserver/service/v1/common" + systemService "management/internal/erpserver/service/v1/system" + "management/internal/pkg/config" + "management/internal/pkg/middleware" + "management/internal/pkg/redis" + "management/internal/pkg/render" + "management/internal/pkg/session" + + "github.com/drhin/logger" + "github.com/go-chi/chi/v5" + "github.com/google/wire" +) + +var repositorySet = wire.NewSet( + repository.NewDB, + repository.NewRepository, + repository.NewTransaction, + systemRepo.NewUserRepository, + systemRepo.NewLoginLogRepository, + systemRepo.NewAuditLogRepository, + systemRepo.NewRoleRepository, + systemRepo.NewMenuRepository, + systemRepo.NewRoleMenuRepository, + systemRepo.NewDepartmentRepository, + systemRepo.NewConfigRepository, +) + +var serviceSet = wire.NewSet( + service.NewService, + commonService.NewCaptchaService, + systemService.NewUserService, + systemService.NewLoginLogService, + systemService.NewAuditLogService, + systemService.NewRoleService, + systemService.NewMenuService, + systemService.NewRoleMenuService, + systemService.NewDepartmentService, + systemService.NewConfigService, +) + +var handlerSet = wire.NewSet( + handler.NewHandler, + commonHandler.NewCaptchaHandler, + commonHandler.NewUploadHandler, + systemHandler.NewHomeHandler, + systemHandler.NewUserHandler, + systemHandler.NewLoginLogHandler, + systemHandler.NewAuditHandler, + systemHandler.NewRoleHandler, + systemHandler.NewMenuHandler, + systemHandler.NewDepartmentHandler, + systemHandler.NewConfigHandler, +) + +var serverSet = wire.NewSet( + NewHTTPServer, +) + +func NewWire(*config.Config, *logger.Logger) (*chi.Mux, func(), error) { + panic(wire.Build( + repositorySet, + redis.New, + session.New, + serviceSet, + middleware.New, + render.New, + handlerSet, + serverSet, + )) +} diff --git a/internal/erpserver/wire_gen.go b/internal/erpserver/wire_gen.go new file mode 100644 index 0000000..38a39b4 --- /dev/null +++ b/internal/erpserver/wire_gen.go @@ -0,0 +1,96 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package erpserver + +import ( + "github.com/drhin/logger" + "github.com/go-chi/chi/v5" + "github.com/google/wire" + "management/internal/erpserver/handler" + common2 "management/internal/erpserver/handler/common" + system3 "management/internal/erpserver/handler/system" + "management/internal/erpserver/repository" + "management/internal/erpserver/repository/system" + "management/internal/erpserver/service" + "management/internal/erpserver/service/v1/common" + system2 "management/internal/erpserver/service/v1/system" + "management/internal/pkg/config" + "management/internal/pkg/middleware" + "management/internal/pkg/redis" + "management/internal/pkg/render" + "management/internal/pkg/session" +) + +// Injectors from wire.go: + +func NewWire(configConfig *config.Config, loggerLogger *logger.Logger) (*chi.Mux, func(), error) { + db, cleanup, err := repository.NewDB(loggerLogger, configConfig) + if err != nil { + return nil, nil, err + } + sessionSession := session.New(db, configConfig) + repositoryRepository := repository.NewRepository(db, loggerLogger) + transaction := repository.NewTransaction(repositoryRepository) + cache, cleanup2, err := redis.New(configConfig, loggerLogger) + if err != nil { + cleanup() + return nil, nil, err + } + serviceService := service.NewService(loggerLogger, transaction, sessionSession, cache) + menuRepository := system.NewMenuRepository(repositoryRepository) + roleRepository := system.NewRoleRepository(repositoryRepository) + roleService := system2.NewRoleService(serviceService, roleRepository) + roleMenuRepository := system.NewRoleMenuRepository(repositoryRepository) + roleMenuService := system2.NewRoleMenuService(serviceService, roleMenuRepository) + menuService := system2.NewMenuService(serviceService, menuRepository, roleService, roleMenuService) + auditLogRepository := system.NewAuditLogRepository(repositoryRepository) + auditLogService := system2.NewAuditLogService(serviceService, auditLogRepository) + middlewareMiddleware := middleware.New(sessionSession, menuService, auditLogService) + renderRender, err := render.New(sessionSession, menuService) + if err != nil { + cleanup2() + cleanup() + return nil, nil, err + } + handlerHandler := handler.NewHandler(configConfig, loggerLogger, middlewareMiddleware, renderRender) + captchaService := common.NewCaptchaService() + captchaHandler := common2.NewCaptchaHandler(handlerHandler, captchaService) + uploadHandler := common2.NewUploadHandler(handlerHandler) + configRepository := system.NewConfigRepository(repositoryRepository) + configService := system2.NewConfigService(serviceService, configRepository) + configHandler := system3.NewConfigHandler(handlerHandler, configService) + userRepository := system.NewUserRepository(repositoryRepository) + loginLogRepository := system.NewLoginLogRepository(repositoryRepository) + loginLogService := system2.NewLoginLogService(serviceService, loginLogRepository) + userService := system2.NewUserService(serviceService, userRepository, roleService, loginLogService) + homeHandler := system3.NewHomeHandler(handlerHandler, userService, loginLogService) + departmentRepository := system.NewDepartmentRepository(repositoryRepository) + departmentService := system2.NewDepartmentService(serviceService, departmentRepository) + userHandler := system3.NewUserHandler(handlerHandler, captchaService, userService, roleService, departmentService) + loginLogHandler := system3.NewLoginLogHandler(handlerHandler, loginLogService) + auditHandler := system3.NewAuditHandler(handlerHandler, auditLogService) + menuHandler := system3.NewMenuHandler(handlerHandler, menuService) + roleHandler := system3.NewRoleHandler(handlerHandler, roleService, menuService) + departmentHandler := system3.NewDepartmentHandler(handlerHandler, departmentService) + mux := NewHTTPServer(middlewareMiddleware, captchaHandler, uploadHandler, configHandler, homeHandler, userHandler, loginLogHandler, auditHandler, menuHandler, roleHandler, departmentHandler) + return mux, func() { + cleanup2() + cleanup() + }, nil +} + +// wire.go: + +var repositorySet = wire.NewSet(repository.NewDB, repository.NewRepository, repository.NewTransaction, system.NewUserRepository, system.NewLoginLogRepository, system.NewAuditLogRepository, system.NewRoleRepository, system.NewMenuRepository, system.NewRoleMenuRepository, system.NewDepartmentRepository, system.NewConfigRepository) + +var serviceSet = wire.NewSet(service.NewService, common.NewCaptchaService, system2.NewUserService, system2.NewLoginLogService, system2.NewAuditLogService, system2.NewRoleService, system2.NewMenuService, system2.NewRoleMenuService, system2.NewDepartmentService, system2.NewConfigService) + +var handlerSet = wire.NewSet(handler.NewHandler, common2.NewCaptchaHandler, common2.NewUploadHandler, system3.NewHomeHandler, system3.NewUserHandler, system3.NewLoginLogHandler, system3.NewAuditHandler, system3.NewRoleHandler, system3.NewMenuHandler, system3.NewDepartmentHandler, system3.NewConfigHandler) + +var serverSet = wire.NewSet( + NewHTTPServer, +) diff --git a/internal/pkg/config/app.go b/internal/pkg/config/app.go deleted file mode 100644 index 8e861b0..0000000 --- a/internal/pkg/config/app.go +++ /dev/null @@ -1,7 +0,0 @@ -package config - -type App struct { - Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务地址 - Port int `mapstructure:"port" json:"port" yaml:"port"` // 服务端口 - Prod bool `mapstructure:"prod" json:"prod" yaml:"prod"` // 是否正式 -} diff --git a/internal/pkg/config/applet.go b/internal/pkg/config/applet.go deleted file mode 100644 index 60bd571..0000000 --- a/internal/pkg/config/applet.go +++ /dev/null @@ -1,6 +0,0 @@ -package config - -type Applet struct { - AppID string `mapstructure:"app_id" json:"app_id" yaml:"app_id"` // appid - AppSecret string `mapstructure:"app_secret" json:"app_secret" yaml:"app_secret"` // secret -} diff --git a/internal/pkg/config/captcha.go b/internal/pkg/config/captcha.go deleted file mode 100644 index 928191a..0000000 --- a/internal/pkg/config/captcha.go +++ /dev/null @@ -1,9 +0,0 @@ -package config - -type Captcha struct { - OpenCaptcha int `mapstructure:"open_captcha" json:"open_captcha" yaml:"open_captcha"` // 是否开启防爆次数 - OpenCaptchaTimeout string `mapstructure:"open_captcha_timeout" json:"open_captcha_timeout" yaml:"open_captcha_timeout"` // 缓存超时时间 - ImgWidth int `mapstructure:"img_width" json:"img_width" yaml:"img_width"` // 验证码图片宽度 - ImgHeight int `mapstructure:"img_height" json:"img_height" yaml:"img_height"` // 验证码图片高度 - KeyLong int `mapstructure:"key_long" json:"key_long" yaml:"key_long"` // 验证码长度 -} diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 1d4f981..6155a72 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -3,91 +3,73 @@ package config import ( "fmt" "path/filepath" + "strings" + "time" "github.com/fsnotify/fsnotify" "github.com/spf13/viper" ) -var File *Config +func New(path string) (*Config, error) { + v := viper.New() + v.AddConfigPath(filepath.Dir(path)) + v.SetConfigName(filepath.Base(path)) + v.SetConfigType(strings.TrimPrefix(filepath.Ext(path), ".")) + if err := v.ReadInConfig(); err != nil { + return nil, err + } -const ConfigDefaultFile = "config.dev.yaml" + v.WatchConfig() + + var config Config + v.OnConfigChange(func(e fsnotify.Event) { + fmt.Println("config file changed:", e.Name) + if err := v.Unmarshal(&config); err != nil { + fmt.Println(err) + } + }) + + if err := v.Unmarshal(&config); err != nil { + return nil, err + } + return &config, nil +} type Config struct { - App App `mapstructure:"app" json:"app" yaml:"app"` - DB DB `mapstructure:"db" json:"db" yaml:"db"` - Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"` - Cors Cors `mapstructure:"cors" json:"cors" yaml:"cors"` - JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"` - AliyunUpload AliyunUpload `mapstructure:"aliyunupload" json:"aliyunupload" yaml:"aliyunupload"` - TencentUpload TencentUpload `mapstructure:"tencentupload" json:"tencentupload" yaml:"tencentupload"` - Captcha Captcha `mapstructure:"captcha" json:"captcha"` - Applet Applet `mapstructure:"applet" json:"applet" yaml:"applet"` - Smb Smb `mapstructure:"smb" json:"smb" yaml:"smb"` -} - -func New(path string) (*Config, error) { - fp := "." - fn := ConfigDefaultFile - if len(path) > 0 { - fp, fn = filepath.Split(path) - if len(fp) == 0 { - fp = "." - } - } - - v := viper.New() - v.AddConfigPath(fp) - v.SetConfigName(fn) - v.SetConfigType("yaml") - if err := v.ReadInConfig(); err != nil { - return nil, err - } - - v.WatchConfig() - - var conf *Config - v.OnConfigChange(func(e fsnotify.Event) { - fmt.Println("config file changed:", e.Name) - if err := v.Unmarshal(&conf); err != nil { - fmt.Println(err) - } - }) - - if err := v.Unmarshal(&conf); err != nil { - return nil, err - } - return conf, nil -} - -func Init(path string) error { - fp := "." - fn := ConfigDefaultFile - if len(path) > 0 { - fp, fn = filepath.Split(path) - if len(fp) == 0 { - fp = "." - } - } - - v := viper.New() - v.AddConfigPath(fp) - v.SetConfigName(fn) - v.SetConfigType("yaml") - if err := v.ReadInConfig(); err != nil { - return err - } - - v.WatchConfig() - - v.OnConfigChange(func(e fsnotify.Event) { - fmt.Println("config file changed:", e.Name) - if err := v.Unmarshal(&File); err != nil { - fmt.Println(err) - } - }) - - if err := v.Unmarshal(&File); err != nil { - return err - } - return nil + App struct { + Host string `mapstructure:"host"` // 服务地址 + Port int `mapstructure:"port"` // 服务端口 + Prod bool `mapstructure:"prod"` // 是否正式 + } `mapstructure:"app"` + DB struct { + Driver string `mapstructure:"driver"` // 数据库类型 + Host string `mapstructure:"host"` // 数据库地址 + Port int `mapstructure:"port"` // 数据库端口 + Username string `mapstructure:"username"` // 数据库用户 + Password string `mapstructure:"password"` // 数据库密码 + DBName string `mapstructure:"db_name"` // 数据库名称 + MaxOpenConns int `mapstructure:"max_open_conns"` // 数据库名称 + MaxIdleConns int `mapstructure:"max_idle_conns"` // 数据库名称 + } `mapstructure:"db"` + Redis struct { + Host string `mapstructure:"host"` // redis地址 + Port int `mapstructure:"port"` // redis端口 + Password string `mapstructure:"password"` // redis密码 + DB int `mapstructure:"db"` // redis数据库 + } `mapstructure:"redis"` + Cors struct { + Host string `mapstructure:"host"` + } `mapstructure:"cors"` + JWT struct { + SigningKey string `mapstructure:"signing_key"` // jwt签名 + ExpiresTime time.Duration `mapstructure:"expires_time"` // 过期时间 + RefreshTime time.Duration `mapstructure:"refresh_time"` // 刷新过期时间 + Issuer string `mapstructure:"issuer"` // 签发者 + } `mapstructure:"jwt"` + Captcha struct { + OpenCaptcha int `mapstructure:"open_captcha"` // 是否开启防爆次数 + ImgWidth int `mapstructure:"img_width"` // 验证码图片宽度 + ImgHeight int `mapstructure:"img_height"` // 验证码图片高度 + KeyLong int `mapstructure:"key_long"` // 验证码长度 + } `mapstructure:"captcha"` } diff --git a/internal/pkg/config/cors.go b/internal/pkg/config/cors.go deleted file mode 100644 index c13ea2b..0000000 --- a/internal/pkg/config/cors.go +++ /dev/null @@ -1,5 +0,0 @@ -package config - -type Cors struct { - Host string `mapstructure:"host" json:"host" yaml:"host"` -} diff --git a/internal/pkg/config/db.go b/internal/pkg/config/db.go deleted file mode 100644 index e9f3699..0000000 --- a/internal/pkg/config/db.go +++ /dev/null @@ -1,12 +0,0 @@ -package config - -type DB struct { - Driver string `mapstructure:"driver" json:"driver" yaml:"driver"` // 数据库类型 - Host string `mapstructure:"host" json:"host" yaml:"host"` // 数据库地址 - Port int `mapstructure:"port" json:"port" yaml:"port"` // 数据库端口 - Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户 - Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码 - DBName string `mapstructure:"db_name" json:"db_name" yaml:"db_name"` // 数据库名称 - MaxOpenConns int `mapstructure:"max_open_conns" json:"max_open_conns" yaml:"max_open_conns"` // 数据库名称 - MaxIdleConns int `mapstructure:"max_idle_conns" json:"max_idle_conns" yaml:"max_idle_conns"` // 数据库名称 -} diff --git a/internal/pkg/config/jwt.go b/internal/pkg/config/jwt.go deleted file mode 100644 index 2956427..0000000 --- a/internal/pkg/config/jwt.go +++ /dev/null @@ -1,10 +0,0 @@ -package config - -import "time" - -type JWT struct { - SigningKey string `mapstructure:"signing_key" json:"signing_key" yaml:"signing_key"` // jwt签名 - ExpiresTime time.Duration `mapstructure:"expires_time" json:"expires_time" yaml:"expires_time"` // 过期时间 - RefreshTime time.Duration `mapstructure:"refresh_time" json:"refresh_time" yaml:"refresh_time"` // 刷新过期时间 - Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者 -} diff --git a/internal/pkg/config/redis.go b/internal/pkg/config/redis.go deleted file mode 100644 index 774ab18..0000000 --- a/internal/pkg/config/redis.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -type Redis struct { - Host string `mapstructure:"host" json:"host" yaml:"host"` // redis地址 - Port int `mapstructure:"port" json:"port" yaml:"port"` // redis端口 - Password string `mapstructure:"password" json:"password" yaml:"password"` // redis密码 - DB int `mapstructure:"db" json:"db_name" yaml:"db"` // redis数据库 -} diff --git a/internal/pkg/config/smb.go b/internal/pkg/config/smb.go deleted file mode 100644 index 80d7308..0000000 --- a/internal/pkg/config/smb.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -type Smb struct { - Host string `mapstructure:"host" json:"host" yaml:"host"` - Name string `mapstructure:"name" json:"name" yaml:"name"` - Pass string `mapstructure:"pass" json:"pass" yaml:"pass"` - Mount string `mapstructure:"mount" json:"mount" yaml:"mount"` -} diff --git a/internal/pkg/config/upload.go b/internal/pkg/config/upload.go deleted file mode 100644 index a55e675..0000000 --- a/internal/pkg/config/upload.go +++ /dev/null @@ -1,18 +0,0 @@ -package config - -type AliyunUpload struct { - Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` - Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` - AccessKeyID string `mapstructure:"access_key_id" json:"access_key_id" yaml:"access_key_id"` - AccessKeySecret string `mapstructure:"access_key_secret" json:"access_key_secret" yaml:"access_key_secret"` -} - -type TencentUpload struct { - Region string `mapstructure:"region" json:"region" yaml:"region"` - Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` - AccessKeyID string `mapstructure:"access_key_id" json:"access_key_id" yaml:"access_key_id"` - AccessKeySecret string `mapstructure:"access_key_secret" json:"access_key_secret" yaml:"access_key_secret"` - AllowImageMaxSize int64 `mapstructure:"allow_image_max_size" json:"allow_image_max_size" yaml:"allow_image_max_size"` - AllowImageExtension string `mapstructure:"allow_image_extension" json:"allow_image_extension" yaml:"allow_image_extension"` - AllowFileMaxSize int64 `mapstructure:"allow_file_max_size" json:"allow_file_max_size" yaml:"allow_file_max_size"` -} diff --git a/internal/pkg/fetcher/fetcher.go b/internal/pkg/fetcher/fetcher.go deleted file mode 100644 index a285a94..0000000 --- a/internal/pkg/fetcher/fetcher.go +++ /dev/null @@ -1,233 +0,0 @@ -package fetcher - -import ( - "bytes" - "errors" - "fmt" - "io" - "mime/multipart" - "net/http" - "time" -) - -// Get get请求 -func Get(url string, timeout time.Duration) ([]byte, int, error) { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, 0, fmt.Errorf("[get] new request err: %v", err) - } - - client := &http.Client{Timeout: timeout} - resp, err := client.Do(req) - if err != nil { - return nil, 0, fmt.Errorf("[get] client do err: %v", err) - } - defer resp.Body.Close() - - status := resp.StatusCode - res, err := io.ReadAll(resp.Body) - if err != nil { - return nil, status, fmt.Errorf("read response err: %v", err) - } - - return res, status, nil -} - -// GetString 请求 -func GetString(url string, parameter map[string]string, timeout time.Duration) ([]byte, int, error) { - byteParameter := new(bytes.Buffer) - w := multipart.NewWriter(byteParameter) - for k, v := range parameter { - w.WriteField(k, v) - } - w.Close() - - request, err := http.NewRequest("GET", url, byteParameter) - if err != nil { - return nil, 0, fmt.Errorf("[getstring] new request err: %v", err) - } - request.Header.Set("Content-Type", w.FormDataContentType()) - - client := &http.Client{Timeout: time.Second * timeout} - response, err := client.Do(request) - if err != nil { - return nil, 0, err - } - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(response.Body) - - status := response.StatusCode - resp, err := io.ReadAll(response.Body) - if err != nil { - return nil, status, err - } - - return resp, status, nil -} - -// GetJson application/json get 请求 -func GetJson(url string, parameter []byte, timeout time.Duration) ([]byte, int, error) { - byteParameter := bytes.NewBuffer(parameter) - req, err := http.NewRequest("GET", url, byteParameter) - if err != nil { - return nil, 0, fmt.Errorf("[getjson] new request err: %v", err) - } - - client := &http.Client{Timeout: timeout} - resp, err := client.Do(req) - if err != nil { - return nil, 0, fmt.Errorf("[getjson] client do err: %v", err) - } - defer resp.Body.Close() - - status := resp.StatusCode - res, err := io.ReadAll(resp.Body) - if err != nil { - return nil, status, fmt.Errorf("read response err: %v", err) - } - - return res, status, nil -} - -// GetJsonWithToken Get json with token 请求 -func GetJsonWithToken(url string, token string, timeout time.Duration) ([]byte, int, error) { - request, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, 0, err - } - request.Header.Set("Content-type", "application/json") - request.Header.Set("Access-Token", token) - - client := &http.Client{Timeout: timeout} - response, err := client.Do(request) - if err != nil { - return nil, 0, errors.New("请求网络错误") - } - defer response.Body.Close() - - status := response.StatusCode - resp, err := io.ReadAll(response.Body) - if err != nil { - return nil, status, err - } - - return resp, status, nil -} - -// PostJson application/json post 请求 -func PostJson(url string, parameter []byte, timeout time.Duration) ([]byte, int, error) { - byteParameter := bytes.NewBuffer(parameter) - request, err := http.NewRequest("POST", url, byteParameter) - if err != nil { - return nil, 0, fmt.Errorf("[postjson] new request err: %v", err) - } - request.Header.Set("Content-type", "application/json") - - client := &http.Client{Timeout: timeout} - response, err := client.Do(request) - if err != nil { - return nil, 0, fmt.Errorf("[postjson] client do err: %v", err) - } - defer response.Body.Close() - - status := response.StatusCode - all, err := io.ReadAll(response.Body) - if err != nil { - return nil, status, fmt.Errorf("read response err: %v", err) - } - - return all, status, nil -} - -// PostString application/x-www-form-urlencoded 请求 -func PostString(url string, parameter []byte, timeout time.Duration) ([]byte, int, error) { - byteParameter := bytes.NewBuffer(parameter) - request, err := http.NewRequest("POST", url, byteParameter) - if err != nil { - return nil, 0, fmt.Errorf("[poststring] new request err: %v", err) - } - request.Header.Set("Content-type", "application/x-www-form-urlencoded") - - client := &http.Client{Timeout: timeout} - response, err := client.Do(request) - if err != nil { - return nil, 0, fmt.Errorf("[poststring] client do err: %v", err) - } - defer response.Body.Close() - - status := response.StatusCode - all, err := io.ReadAll(response.Body) - if err != nil { - return nil, status, fmt.Errorf("read response err: %v", err) - } - - return all, status, nil -} - -// PostJsonWithToken Post json with token 请求 -func PostJsonWithToken(url string, parameter []byte, token string, timeout time.Duration) ([]byte, int, error) { - bufParameter := bytes.NewBuffer(parameter) - request, err := http.NewRequest("POST", url, bufParameter) - if err != nil { - return nil, 0, err - } - request.Header.Set("Content-type", "application/json") - request.Header.Set("Access-Token", token) - - client := &http.Client{Timeout: timeout} - response, err := client.Do(request) - if err != nil { - return nil, 0, fmt.Errorf("请求网络错误: %v", err) - } - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(response.Body) - - stauts := response.StatusCode - resp, err := io.ReadAll(response.Body) - if err != nil { - return nil, stauts, errors.New("网络请求结果读取失败") - } - - return resp, stauts, nil -} - -// PostJsonWithBearerToken Post json with bearer token 请求 -func PostJsonWithBearerToken(url string, parameter []byte, token string, timeout time.Duration) ([]byte, int, error) { - bufParameter := bytes.NewBuffer(parameter) - request, err := http.NewRequest("POST", url, bufParameter) - if err != nil { - return nil, 0, err - } - request.Header.Set("Content-type", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("bearer %s", token)) - - client := &http.Client{Timeout: timeout} - response, err := client.Do(request) - if err != nil { - return nil, 0, fmt.Errorf("请求网络错误: %v", err) - } - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(response.Body) - - status := response.StatusCode - resp, err := io.ReadAll(response.Body) - if err != nil { - return nil, status, errors.New("网络请求结果读取失败") - } - - return resp, status, nil -} - -// func determiningEncoding(r *bufio.Reader) encoding.Encoding { -// b, err := r.Peek(1024) -// if err != nil && err != io.EOF { -// log.Printf("Fetcher error: %v", err) -// return unicode.UTF8 -// } - -// e, _, _ := charset.DetermineEncoding(b, "") -// return e -// } diff --git a/internal/pkg/gin/gu/cors.go b/internal/pkg/gin/gu/cors.go deleted file mode 100644 index 47e230a..0000000 --- a/internal/pkg/gin/gu/cors.go +++ /dev/null @@ -1,26 +0,0 @@ -package gu - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -func Cors() gin.HandlerFunc { - return func(c *gin.Context) { - method := c.Request.Method - - c.Header("Access-Control-Allow-Origin", "*") - c.Header("Access-Control-Allow-Headers", "Content-Type, AccessToken, X-CSRF-Token, Authorization, Token") - c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") - c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") - c.Header("Access-Control-Allow-Credentials", "true") - - // 放行所有OPTIONS方法 - if method == "OPTIONS" { - c.AbortWithStatus(http.StatusNoContent) - } - // 处理请求 - c.Next() - } -} diff --git a/internal/pkg/gin/gu/response.go b/internal/pkg/gin/gu/response.go deleted file mode 100644 index 1ecc94c..0000000 --- a/internal/pkg/gin/gu/response.go +++ /dev/null @@ -1,40 +0,0 @@ -package gu - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -type response struct { - Code int `json:"code"` - Message string `json:"message"` - Data any `json:"data"` -} - -type PageData struct { - Total int64 `json:"total"` - PageID int32 `json:"page_id"` - PageSize int32 `json:"page_size"` - Result any `json:"result"` -} - -func Ok(ctx *gin.Context, data any) { - ResponseJson(ctx, http.StatusOK, "ok", data) -} - -func Failed(ctx *gin.Context, message string) { - ResponseJson(ctx, http.StatusInternalServerError, message, nil) -} - -func FailedWithCode(ctx *gin.Context, code int, message string) { - ResponseJson(ctx, code, message, nil) -} - -func ResponseJson(ctx *gin.Context, code int, message string, data any) { - ctx.JSON(code, response{ - Code: code, - Message: message, - Data: data, - }) -} diff --git a/internal/pkg/gin/gu/validator.go b/internal/pkg/gin/gu/validator.go deleted file mode 100644 index f3cba5a..0000000 --- a/internal/pkg/gin/gu/validator.go +++ /dev/null @@ -1,57 +0,0 @@ -package gu - -import ( - "fmt" - "net/http" - "strings" - - "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" - "github.com/go-playground/locales/en" - "github.com/go-playground/locales/zh" - ut "github.com/go-playground/universal-translator" - "github.com/go-playground/validator/v10" - enTranslations "github.com/go-playground/validator/v10/translations/en" - chTranslations "github.com/go-playground/validator/v10/translations/zh" -) - -var trans ut.Translator - -// loca 通常取决于 http 请求头的 'Accept-Language' -func SetValidatorTrans(local string) (err error) { - if v, ok := binding.Validator.Engine().(*validator.Validate); ok { - zhT := zh.New() // chinese - enT := en.New() // english - uni := ut.New(enT, zhT, enT) - - var o bool - trans, o = uni.GetTranslator(local) - if !o { - return fmt.Errorf("uni.GetTranslator(%s) failed", local) - } - // register translate - // 注册翻译器 - switch local { - case "en": - err = enTranslations.RegisterDefaultTranslations(v, trans) - case "zh": - err = chTranslations.RegisterDefaultTranslations(v, trans) - default: - err = enTranslations.RegisterDefaultTranslations(v, trans) - } - return - } - return -} - -func ValidatorErrors(ctx *gin.Context, err error) { - if errors, ok := err.(validator.ValidationErrors); ok { - errs := gin.H{} - for _, e := range errors { - errs[e.StructField()] = strings.Replace(e.Translate(trans), e.StructField(), "", -1) - } - ctx.JSON(http.StatusBadRequest, errs) - } else { - ctx.JSON(http.StatusBadRequest, err) - } -} diff --git a/internal/pkg/know/know.go b/internal/pkg/know/know.go index aa2b4ee..8ab16c0 100644 --- a/internal/pkg/know/know.go +++ b/internal/pkg/know/know.go @@ -21,35 +21,35 @@ const ( ) var ( - // pear admin 配置 - PearAdmin = "m:pearjson" + // PearAdmin 配置 + PearAdmin = "m:pear_json" - // 所有类别 + // AllCategories 所有类别 AllCategories = "m:category:all" - // 所有类别 简单信息 - AllCategorySimple = "m:categorysimple:all" - // 类别列表 根据 父id 获取 + // AllCategorySimple 所有类别 简单信息 + AllCategorySimple = "m:category_simple:all" + // ListCategoriesByParentID 类别列表 根据 父id 获取 ListCategoriesByParentID = "m:category:parent_id:%d" - // 所有部门 + // AllDepartments 所有部门 AllDepartments = "m:department:all" - // 所有菜单 + // AllMenus 所有菜单 AllMenus = "m:menus:all" - // 递归菜单 + // RecursiveMenus 递归菜单 RecursiveMenus = "m:rec_menus:%d" - // 根据用户ID获取菜单 + // AdminMenus 根据用户ID获取菜单 AdminMenus = "m:admin_menus:%d" - // 登陆用户的菜单 + // OwnerMenus 登陆用户的菜单 OwnerMenus = "m:owner_menus:%d" - // 登陆用户的菜单 + // OwnerMenusMap 登陆用户的菜单 OwnerMenusMap = "m:owner_menus_map:%d" - // 所有角色 + // AllRoles 所有角色 AllRoles = "m:role:all" ) -func GetManageKey(ctx context.Context, key string, arg ...any) string { +func GetManageKey(_ context.Context, key string, arg ...any) string { key = fmt.Sprintf(key, arg...) return key } diff --git a/internal/pkg/logger/log.go b/internal/pkg/logger/log.go deleted file mode 100644 index ef8b8ec..0000000 --- a/internal/pkg/logger/log.go +++ /dev/null @@ -1,54 +0,0 @@ -package logger - -import ( - "os" - "time" - - "management/internal/pkg/config" - - "github.com/natefinch/lumberjack" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" -) - -func New(prod bool) { - zerolog.SetGlobalLevel(zerolog.InfoLevel) - logRotate := &lumberjack.Logger{ - Filename: "./log/run.log", // 日志文件的位置 - MaxSize: 10, // 在进行切割之前,日志文件的最大大小(以MB为单位) - MaxBackups: 100, // 保留旧文件的最大个数 - MaxAge: 30, // 保留旧文件的最大天数 - Compress: true, - } - zerolog.TimeFieldFormat = time.DateTime - log.Logger = log.With().Caller().Logger() - - if prod { - log.Logger = log.Output(logRotate) - } else { - consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.DateTime} - multi := zerolog.MultiLevelWriter(consoleWriter, logRotate) - log.Logger = log.Output(multi) - } -} - -func Init() { - zerolog.SetGlobalLevel(zerolog.InfoLevel) - logRotate := &lumberjack.Logger{ - Filename: "./log/run.log", // 日志文件的位置 - MaxSize: 10, // 在进行切割之前,日志文件的最大大小(以MB为单位) - MaxBackups: 100, // 保留旧文件的最大个数 - MaxAge: 30, // 保留旧文件的最大天数 - Compress: true, - } - zerolog.TimeFieldFormat = time.DateTime - log.Logger = log.With().Caller().Logger() - - if config.File.App.Prod { - log.Logger = log.Output(logRotate) - } else { - consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.DateTime} - multi := zerolog.MultiLevelWriter(consoleWriter, logRotate) - log.Logger = log.Output(multi) - } -} diff --git a/internal/pkg/middleware/audit.go b/internal/pkg/middleware/audit.go index 9b9e26b..9639137 100644 --- a/internal/pkg/middleware/audit.go +++ b/internal/pkg/middleware/audit.go @@ -29,5 +29,5 @@ func (m *middleware) writeLog(req *http.Request, start time.Time) { c, cancel := context.WithTimeout(context.Background(), time.Second*3) defer cancel() - _ = m.auditLogsvc.Create(c, al) + _ = m.auditLogService.Create(c, al) } diff --git a/internal/pkg/middleware/authorize.go b/internal/pkg/middleware/authorize.go index 3d8d198..f7ff130 100644 --- a/internal/pkg/middleware/authorize.go +++ b/internal/pkg/middleware/authorize.go @@ -28,21 +28,14 @@ func (m *middleware) Authorize(next http.Handler) http.Handler { return } - if user == nil { - http.Error(w, "user not found", http.StatusUnauthorized) - return - } - // 登陆成功 判断权限 - - // 默认权限判断 path := r.URL.Path if b, ok := defaultMenus[path]; ok && b { next.ServeHTTP(w, r) return } - menus, err := m.menusvc.ListByRoleIDToMap(ctx, user.RoleID) + menus, err := m.menuService.ListByRoleIDToMap(ctx, user.RoleID) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -63,10 +56,9 @@ func (m *middleware) isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) { if exists := m.session.Exists(ctx, know.StoreName); exists { b := m.session.GetBytes(ctx, know.StoreName) var user dto.AuthorizeUser - if err := json.Unmarshal(b, &user); err != nil { - return nil, false + if err := json.Unmarshal(b, &user); err == nil && user.ID > 0 { + return &user, true } - return &user, true } return nil, false @@ -84,7 +76,7 @@ func (m *middleware) AuthUser(ctx context.Context) dto.AuthorizeUser { func (m *middleware) IsAuth(ctx context.Context) bool { var user dto.AuthorizeUser b := m.session.GetBytes(ctx, know.StoreName) - if err := json.Unmarshal(b, &user); err == nil { + if err := json.Unmarshal(b, &user); err == nil && user.ID > 0 { return true } return false diff --git a/internal/pkg/middleware/middleware.go b/internal/pkg/middleware/middleware.go index cddb398..01817d6 100644 --- a/internal/pkg/middleware/middleware.go +++ b/internal/pkg/middleware/middleware.go @@ -21,17 +21,15 @@ type Middleware interface { } type middleware struct { - session session.Session - menusvc v1.MenuService - auditLogsvc v1.AuditLogService + session session.Session + menuService v1.MenuService + auditLogService v1.AuditLogService } -var _ Middleware = (*middleware)(nil) - -func New(session session.Session, menusvc v1.MenuService, auditLogsvc v1.AuditLogService) Middleware { +func New(session session.Session, menuService v1.MenuService, auditLogService v1.AuditLogService) Middleware { return &middleware{ - session: session, - menusvc: menusvc, - auditLogsvc: auditLogsvc, + session: session, + menuService: menuService, + auditLogService: auditLogService, } } diff --git a/internal/pkg/redis/redis.go b/internal/pkg/redis/redis.go index 962b2c7..0c7c749 100644 --- a/internal/pkg/redis/redis.go +++ b/internal/pkg/redis/redis.go @@ -10,12 +10,13 @@ import ( "management/internal/pkg/config" + "github.com/drhin/logger" "github.com/redis/go-redis/v9" ) var ErrRedisKeyNotFound = errors.New("redis key not found") -type RedisCache interface { +type Cache interface { Encode(a any) ([]byte, error) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error Del(ctx context.Context, keys ...string) error @@ -27,25 +28,29 @@ type RedisCache interface { } type redisCache struct { - engine *redis.Client + client *redis.Client } -var _ RedisCache = (*redisCache)(nil) - -func New(conf config.Redis) (*redisCache, error) { +func New(conf *config.Config, log *logger.Logger) (Cache, func(), error) { rdb := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%s:%d", conf.Host, conf.Port), - Password: conf.Password, - DB: conf.DB, + Addr: fmt.Sprintf("%s:%d", conf.Redis.Host, conf.Redis.Port), + Password: conf.Redis.Password, + DB: conf.Redis.DB, }) _, err := rdb.Ping(context.Background()).Result() if err != nil { - return nil, err + return nil, nil, err + } + + cleanup := func() { + if err := rdb.Close(); err != nil { + log.Error("redis close error", err) + } } return &redisCache{ - engine: rdb, - }, nil + client: rdb, + }, cleanup, nil } func (r *redisCache) Encode(a any) ([]byte, error) { @@ -59,18 +64,18 @@ func (r *redisCache) Encode(a any) ([]byte, error) { // Set 设置值 func (r *redisCache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error { - return r.engine.Set(ctx, key, value, expiration).Err() + return r.client.Set(ctx, key, value, expiration).Err() } // Del 删除键值 func (r *redisCache) Del(ctx context.Context, keys ...string) error { - return r.engine.Del(ctx, keys...).Err() + return r.client.Del(ctx, keys...).Err() } // Get 获取值 func (r *redisCache) Get(ctx context.Context, key string) (string, error) { - val, err := r.engine.Get(ctx, key).Result() - if err == redis.Nil { + val, err := r.client.Get(ctx, key).Result() + if errors.Is(err, redis.Nil) { return "", ErrRedisKeyNotFound } else if err != nil { return "", fmt.Errorf("cannot get value with:[%s]: %v", key, err) @@ -81,8 +86,8 @@ func (r *redisCache) Get(ctx context.Context, key string) (string, error) { // GetBytes 获取值 func (r *redisCache) GetBytes(ctx context.Context, key string) ([]byte, error) { - val, err := r.engine.Get(ctx, key).Bytes() - if err == redis.Nil { + val, err := r.client.Get(ctx, key).Bytes() + if errors.Is(err, redis.Nil) { return nil, ErrRedisKeyNotFound } else if err != nil { return nil, fmt.Errorf("cannot get value with:[%s]: %v", key, err) @@ -92,15 +97,15 @@ func (r *redisCache) GetBytes(ctx context.Context, key string) ([]byte, error) { } func (r *redisCache) Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd { - return r.engine.Scan(ctx, cursor, match, count) + return r.client.Scan(ctx, cursor, match, count) } func (r *redisCache) Keys(ctx context.Context, pattern string) ([]string, error) { - return r.engine.Keys(ctx, pattern).Result() + return r.client.Keys(ctx, pattern).Result() } func (r *redisCache) ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]string, int, error) { - all, err := r.engine.Keys(ctx, pattern).Result() + all, err := r.client.Keys(ctx, pattern).Result() if err != nil { return nil, 0, err } @@ -115,7 +120,7 @@ func (r *redisCache) ListKeys(ctx context.Context, pattern string, pageID int, p for { var scanResult []string var err error - scanResult, cursor, err = r.engine.Scan(ctx, cursor, pattern, int64(pageSize)).Result() + scanResult, cursor, err = r.client.Scan(ctx, cursor, pattern, int64(pageSize)).Result() if err != nil { return nil, count, err } @@ -135,36 +140,3 @@ func (r *redisCache) ListKeys(ctx context.Context, pattern string, pageID int, p } return keys[startIndex:endIndex], count, nil } - -// ========================== -func Encode(a any) ([]byte, error) { - return nil, nil -} - -func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error { - return nil -} - -func Del(ctx context.Context, keys ...string) error { - return nil -} - -func Get(ctx context.Context, key string) (string, error) { - return "", nil -} - -func GetBytes(ctx context.Context, key string) ([]byte, error) { - return nil, nil -} - -func Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd { - return nil -} - -func Keys(ctx context.Context, pattern string) ([]string, error) { - return nil, nil -} - -func ListKeys(ctx context.Context, pattern string, pageID int, pageSize int) ([]string, int, error) { - return nil, 0, nil -} diff --git a/internal/pkg/tpl/html_method.go b/internal/pkg/render/funcs/method.go similarity index 91% rename from internal/pkg/tpl/html_method.go rename to internal/pkg/render/funcs/method.go index 8505ada..a5c989a 100644 --- a/internal/pkg/tpl/html_method.go +++ b/internal/pkg/render/funcs/method.go @@ -1,4 +1,4 @@ -package tpl +package funcs import ( "html/template" @@ -6,7 +6,7 @@ import ( "time" ) -func (r *render) Methods() map[string]any { +func Methods() map[string]any { res := make(map[string]any, 1) res["dateFormat"] = func(dt time.Time) template.HTML { @@ -45,10 +45,10 @@ func (r *render) Methods() map[string]any { res["expandTags"] = func(s []string) template.HTML { if len(s) == 0 { - return template.HTML("") + return "" } if len(s) == 1 && s[0] == "all" { - return template.HTML("") + return "" } return template.HTML(strings.Join(s, ",")) } diff --git a/internal/pkg/tpl/html_btn.go b/internal/pkg/render/gen/btn.go similarity index 77% rename from internal/pkg/tpl/html_btn.go rename to internal/pkg/render/gen/btn.go index 2928cd1..5b270c6 100644 --- a/internal/pkg/tpl/html_btn.go +++ b/internal/pkg/render/gen/btn.go @@ -1,4 +1,4 @@ -package tpl +package gen import ( "html/template" @@ -8,17 +8,17 @@ import ( "management/internal/erpserver/model/dto" ) -func (r *render) btnFuncs() map[string]any { +func Button() map[string]any { res := make(map[string]any, 3) - res["genBtn"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML { - if len(btns) == 0 { - return template.HTML("") + res["genBtn"] = func(buttons []*dto.OwnerMenuDto, actionNames ...string) template.HTML { + if len(buttons) == 0 { + return "" } var res string for _, action := range actionNames { - for _, btn := range btns { + for _, btn := range buttons { btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui") base := filepath.Base(btn.Url) if base == action { @@ -34,14 +34,14 @@ func (r *render) btnFuncs() map[string]any { return template.HTML(res) } - res["genLink"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML { - if len(btns) == 0 { - return template.HTML("") + res["genLink"] = func(buttons []*dto.OwnerMenuDto, actionNames ...string) template.HTML { + if len(buttons) == 0 { + return "" } var res string for _, action := range actionNames { - for _, btn := range btns { + for _, btn := range buttons { btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui") base := filepath.Base(btn.Url) if base == action { @@ -64,14 +64,14 @@ func (r *render) btnFuncs() map[string]any { return template.HTML(res) } - res["submitBtn"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML { - if len(btns) == 0 { - return template.HTML("") + res["submitBtn"] = func(buttons []*dto.OwnerMenuDto, actionNames ...string) template.HTML { + if len(buttons) == 0 { + return "" } var res string for _, action := range actionNames { - for _, btn := range btns { + for _, btn := range buttons { btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui") base := filepath.Base(btn.Url) if base == action { @@ -89,3 +89,11 @@ func (r *render) btnFuncs() map[string]any { return res } + +func firstLower(s string) string { + if len(s) == 0 { + return s + } + + return strings.ToLower(s[:1]) + s[1:] +} diff --git a/internal/pkg/render/html.go b/internal/pkg/render/html.go new file mode 100644 index 0000000..6fd96c6 --- /dev/null +++ b/internal/pkg/render/html.go @@ -0,0 +1,106 @@ +package render + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "html/template" + "net/http" + "path/filepath" + "strings" + + "management/internal/erpserver/model/dto" + "management/internal/pkg/know" + + "github.com/justinas/nosurf" +) + +type TemplateConfig struct { + Root string + Extension string + Layout string + Partial string +} + +type HtmlData struct { + IsAuthenticated bool + AuthorizeUser dto.AuthorizeUser + AuthorizeMenus []*dto.OwnerMenuDto + Data any +} + +func (r *render) HTML(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) { + name := strings.ReplaceAll(tpl, "/", "_") + t, ok := r.templates[name] + if !ok { + http.Error(w, "template is empty", http.StatusInternalServerError) + return + } + + hd := r.setDefaultData(req, data) + + buf := new(bytes.Buffer) + err := t.ExecuteTemplate(buf, filepath.Base(tpl), hd) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + _, err = buf.WriteTo(w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} + +func (r *render) setDefaultData(req *http.Request, data map[string]any) map[string]any { + if data == nil { + data = make(map[string]any) + } + + ctx := req.Context() + isAuth := r.session.Exists(ctx, know.StoreName) + data["IsAuthenticated"] = isAuth + if isAuth { + var authUser dto.AuthorizeUser + u := r.session.GetBytes(ctx, know.StoreName) + _ = json.Unmarshal(u, &authUser) + + data["AuthorizeMenus"] = r.getCurrentPathButtons(ctx, authUser.RoleID, req.URL.Path) + } + token := nosurf.Token(req) + data["CsrfToken"] = token + data["CsrfTokenField"] = template.HTML(fmt.Sprintf(``, token)) + + return data +} + +func (r *render) getCurrentPathButtons(ctx context.Context, roleID int32, path string) []*dto.OwnerMenuDto { + var res []*dto.OwnerMenuDto + + // 获取当前登陆角色的权限 + menus, err := r.menuService.ListByRoleIDToMap(ctx, roleID) + if err != nil { + return res + } + + menu, ok := menus[path] + if !ok { + return res + } + + for _, item := range menus { + 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 +} diff --git a/internal/pkg/tpl/html/select.go b/internal/pkg/render/html/select.go similarity index 100% rename from internal/pkg/tpl/html/select.go rename to internal/pkg/render/html/select.go diff --git a/internal/pkg/tpl/html/vars.go b/internal/pkg/render/html/vars.go similarity index 100% rename from internal/pkg/tpl/html/vars.go rename to internal/pkg/render/html/vars.go diff --git a/internal/pkg/tpl/json.go b/internal/pkg/render/json.go similarity index 69% rename from internal/pkg/tpl/json.go rename to internal/pkg/render/json.go index 8d8ed4e..7dcbef7 100644 --- a/internal/pkg/tpl/json.go +++ b/internal/pkg/render/json.go @@ -1,4 +1,4 @@ -package tpl +package render import ( "encoding/json" @@ -11,12 +11,12 @@ type Response struct { Data any `json:"data"` } -type ResponseDtree struct { - Status ResponseDtreeStatus `json:"status"` - Data any `json:"data"` +type ResponseTree struct { + Status ResponseTreeStatus `json:"status"` + Data any `json:"data"` } -type ResponseDtreeStatus struct { +type ResponseTreeStatus struct { Code int `json:"code"` Message string `json:"message"` } @@ -28,18 +28,18 @@ type ResponseList struct { Data any `json:"data"` } -func (r *render) JSONF(w http.ResponseWriter, success bool, message string) { - r.JSON(w, Response{Success: success, Message: message}) -} - -func (r *render) JSONOK(w http.ResponseWriter, message string) { +func (r *render) JSONOk(w http.ResponseWriter, message string) { r.JSON(w, Response{Success: true, Message: message}) } -func (r *render) JSONERR(w http.ResponseWriter, message string) { +func (r *render) JSONErr(w http.ResponseWriter, message string) { r.JSON(w, Response{Success: false, Message: message}) } +func (r *render) JSONObj(w http.ResponseWriter, message string, data any) { + r.JSON(w, Response{Success: true, Message: message, Data: data}) +} + func (r *render) JSON(w http.ResponseWriter, data any) { v, err := json.Marshal(data) if err != nil { diff --git a/internal/pkg/render/render.go b/internal/pkg/render/render.go new file mode 100644 index 0000000..5dd0e0f --- /dev/null +++ b/internal/pkg/render/render.go @@ -0,0 +1,60 @@ +package render + +import ( + "html/template" + "net/http" + + v1 "management/internal/erpserver/service/v1" + "management/internal/pkg/render/util" + "management/internal/pkg/session" +) + +type Render interface { + htmlRender + jsonRender +} + +type htmlRender interface { + HTML(w http.ResponseWriter, req *http.Request, name string, data map[string]any) +} + +type jsonRender interface { + JSON(w http.ResponseWriter, data any) + JSONObj(w http.ResponseWriter, message string, data any) + JSONOk(w http.ResponseWriter, message string) + JSONErr(w http.ResponseWriter, message string) +} + +type render struct { + templateConfig *TemplateConfig + templates map[string]*template.Template + session session.Session + + menuService v1.MenuService +} + +func New(session session.Session, menuService v1.MenuService) (Render, error) { + r := &render{ + templateConfig: &TemplateConfig{ + Root: ".", + Extension: ".tmpl", + Layout: "base", + Partial: "partial", + }, + session: session, + menuService: menuService, + } + + var err error + r.templates, err = util.CreateTemplateCache( + r.templateConfig.Root, + r.templateConfig.Layout, + r.templateConfig.Partial, + r.templateConfig.Extension, + ) + if err != nil { + return nil, err + } + + return r, nil +} diff --git a/internal/pkg/render/util/util.go b/internal/pkg/render/util/util.go new file mode 100644 index 0000000..3ec638f --- /dev/null +++ b/internal/pkg/render/util/util.go @@ -0,0 +1,99 @@ +package util + +import ( + "fmt" + "html/template" + "io/fs" + "os" + "path/filepath" + "slices" + "strings" + + "management/internal/pkg/render/funcs" + "management/internal/pkg/render/gen" + templates "management/web/templates/manage" +) + +func CreateTemplateCache(root, layout, partial, extension string) (map[string]*template.Template, error) { + cache := make(map[string]*template.Template) + pages, err := getFiles(root, extension) + if err != nil { + return nil, err + } + + layoutAndPartial, err := getLayoutAndPartials(layout, partial, extension) + if err != nil { + return nil, err + } + + for _, page := range pages { + if strings.HasPrefix(page, layout) || strings.HasSuffix(page, partial) { + continue + } + + name := filepath.Base(page) + pathArr := strings.Split(page, "/") + dir := pathArr[len(pathArr)-2 : len(pathArr)-1] + templateName := fmt.Sprintf("%s_%s", dir[0], name) + ts := template.Must(template.New(templateName).Funcs(gen.Button()).Funcs(funcs.Methods()).ParseFS(templates.TemplateFS, page)) + ts, err = ts.ParseFS(templates.TemplateFS, layoutAndPartial...) + if err != nil { + return nil, err + } + + cache[templateName] = ts + } + + return cache, nil +} + +func getLayoutAndPartials(layout, partial, extension string) ([]string, error) { + layouts, err := getFiles(layout, extension) + if err != nil { + return nil, err + } + + partials, err := getFiles(partial, extension) + if err != nil { + return nil, err + } + + return slices.Concat(layouts, partials), nil +} + +func getFiles(path string, suffix string) ([]string, error) { + files := make([]string, 0) + b, err := pathExists(templates.TemplateFS, path) + if err != nil { + return nil, err + } + + if !b { + return files, nil + } + + err = fs.WalkDir(templates.TemplateFS, path, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + if strings.HasSuffix(path, suffix) { + files = append(files, path) + } + return nil + }) + return files, err +} + +func pathExists(fs fs.FS, path string) (bool, error) { + _, err := fs.Open(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, err +} diff --git a/internal/pkg/session/session.go b/internal/pkg/session/session.go index dad7ad5..d5ecb60 100644 --- a/internal/pkg/session/session.go +++ b/internal/pkg/session/session.go @@ -2,12 +2,14 @@ package session import ( "context" - "database/sql" "net/http" "time" + "management/internal/pkg/config" + "github.com/alexedwards/scs/postgresstore" "github.com/alexedwards/scs/v2" + "gorm.io/gorm" ) type Session interface { @@ -23,7 +25,7 @@ type session struct { sessionManager *scs.SessionManager } -func New(db *sql.DB, prod bool) Session { +func New(db *gorm.DB, config *config.Config) Session { sessionManager := scs.New() sessionManager.Lifetime = 24 * time.Hour sessionManager.IdleTimeout = 2 * time.Hour @@ -31,11 +33,12 @@ func New(db *sql.DB, prod bool) Session { sessionManager.Cookie.HttpOnly = true sessionManager.Cookie.Persist = true sessionManager.Cookie.SameSite = http.SameSiteStrictMode - sessionManager.Cookie.Secure = prod + sessionManager.Cookie.Secure = config.App.Prod + sqlDB, _ := db.DB() // postgres // github.com/alexedwards/scs/postgresstore - sessionManager.Store = postgresstore.New(db) + sessionManager.Store = postgresstore.New(sqlDB) // pgx // github.com/alexedwards/scs/pgxstore // sessionManager.Store = pgxstore.New(pool) diff --git a/internal/pkg/sliceutil/sliceutil.go b/internal/pkg/sliceutil/sliceutil.go index e6f82aa..3eeeb2e 100644 --- a/internal/pkg/sliceutil/sliceutil.go +++ b/internal/pkg/sliceutil/sliceutil.go @@ -2,7 +2,7 @@ package sliceutil import "sort" -// 使用 map 去除重复元素 +// RemoveDuplicatesWithMap 使用 map 去除重复元素 func RemoveDuplicatesWithMap[T comparable](slice []T) []T { result := make([]T, 0, len(slice)) seen := make(map[T]bool) @@ -15,7 +15,7 @@ func RemoveDuplicatesWithMap[T comparable](slice []T) []T { return result } -// 先排序再去重 +// RemoveDuplicatesWithSort 先排序再去重 func RemoveDuplicatesWithSort(slice []int) []int { if len(slice) == 0 { return slice diff --git a/internal/pkg/smb/smb.go b/internal/pkg/smb/smb.go deleted file mode 100644 index f12daea..0000000 --- a/internal/pkg/smb/smb.go +++ /dev/null @@ -1,41 +0,0 @@ -package smb - -import ( - "io/fs" - "net" - - "management/internal/pkg/config" - - "github.com/hirochachacha/go-smb2" -) - -var FS fs.FS - -func Init() error { - conn, err := net.Dial("tcp", config.File.Smb.Host+":445") - if err != nil { - return err - } - defer conn.Close() - - d := &smb2.Dialer{ - Initiator: &smb2.NTLMInitiator{ - User: config.File.Smb.Name, - Password: config.File.Smb.Pass, - }, - } - s, err := d.Dial(conn) - if err != nil { - return err - } - defer s.Logoff() - - fs, err := s.Mount(config.File.Smb.Mount) - if err != nil { - return err - } - defer fs.Umount() - - FS = fs.DirFS(".") - return nil -} diff --git a/internal/pkg/snowflake/snowflake.go b/internal/pkg/snowflake/snowflake.go deleted file mode 100644 index ccace28..0000000 --- a/internal/pkg/snowflake/snowflake.go +++ /dev/null @@ -1,20 +0,0 @@ -package snowflake - -import ( - "github.com/bwmarrin/snowflake" -) - -var node *snowflake.Node - -func Init() error { - n, err := snowflake.NewNode(1) - if err != nil { - return err - } - node = n - return nil -} - -func GetId() int64 { - return node.Generate().Int64() -} diff --git a/internal/pkg/sqids/sqids.go b/internal/pkg/sqids/sqids.go deleted file mode 100644 index 13353d0..0000000 --- a/internal/pkg/sqids/sqids.go +++ /dev/null @@ -1,25 +0,0 @@ -package sqids - -import "github.com/sqids/sqids-go" - -var engine *sqids.Sqids - -func Init() error { - var err error - engine, err = sqids.New(sqids.Options{ - MinLength: 12, - Alphabet: "AvjM1lkB8N6cuhs2oFxnXyYDwCmLGI7JOzt9fW3HRgb5ZQrqaU04TePSVKdpiE", - }) - if err != nil { - return err - } - return nil -} - -func Encode(ids []uint64) (string, error) { - return engine.Encode(ids) -} - -func Decode(s string) []uint64 { - return engine.Decode(s) -} diff --git a/internal/pkg/token/jwt_maker.go b/internal/pkg/token/jwt_maker.go deleted file mode 100644 index 0d9abe8..0000000 --- a/internal/pkg/token/jwt_maker.go +++ /dev/null @@ -1,59 +0,0 @@ -package token - -import ( - "fmt" - "time" - - tk "github.com/golang-jwt/jwt/v5" -) - -const minSecretKeySize = 32 - -// JWTMaker JSON Web Token -type JWTMaker struct { - secretKey string -} - -// NewJWTMaker 创建一个新的JWTMaker -func NewJWTMaker(secretKey string) error { - if len(secretKey) < minSecretKeySize { - return fmt.Errorf("invalid key size: must be at least %d characters", minSecretKeySize) - } - engine = &JWTMaker{secretKey} - return nil -} - -// CreateToken 根据用户名和时间创建一个新的token -func (maker *JWTMaker) CreateToken(id string, username string, duration time.Duration) (string, *Payload, error) { - payload, err := NewPayload(id, username, duration) - if err != nil { - return "", payload, err - } - - jwtToken := tk.NewWithClaims(tk.SigningMethodHS256, payload) - token, err := jwtToken.SignedString([]byte(maker.secretKey)) - return token, payload, err -} - -// VerifyToken checks if the token is valid or not -func (maker *JWTMaker) VerifyToken(token string) (*Payload, error) { - keyFunc := func(token *tk.Token) (interface{}, error) { - _, ok := token.Method.(*tk.SigningMethodHMAC) - if !ok { - return nil, ErrInvalidToken - } - return []byte(maker.secretKey), nil - } - - jwtToken, err := tk.ParseWithClaims(token, &Payload{}, keyFunc) - if err != nil { - return nil, ErrInvalidToken - } - - payload, ok := jwtToken.Claims.(*Payload) - if !ok { - return nil, ErrInvalidToken - } - - return payload, nil -} diff --git a/internal/pkg/token/maker.go b/internal/pkg/token/maker.go deleted file mode 100644 index d3d5b81..0000000 --- a/internal/pkg/token/maker.go +++ /dev/null @@ -1,24 +0,0 @@ -package token - -import ( - "time" -) - -var engine Maker - -// Maker 管理token的接口定义 -type Maker interface { - // CreateToken 根据用户名和时间创建一个新的token - CreateToken(id string, username string, duration time.Duration) (string, *Payload, error) - - // VerifyToken 校验token是否正确 - VerifyToken(token string) (*Payload, error) -} - -func CreateToken(id string, username string, duration time.Duration) (string, *Payload, error) { - return engine.CreateToken(id, username, duration) -} - -func VerifyToken(token string) (*Payload, error) { - return engine.VerifyToken(token) -} diff --git a/internal/pkg/token/paseto_maker.go b/internal/pkg/token/paseto_maker.go deleted file mode 100644 index 2ae494b..0000000 --- a/internal/pkg/token/paseto_maker.go +++ /dev/null @@ -1,60 +0,0 @@ -package token - -import ( - "fmt" - "time" - - "management/internal/pkg/config" - - "github.com/aead/chacha20poly1305" - "github.com/o1egl/paseto" -) - -// PasetoMaker is a PASETO token maker -type PasetoMaker struct { - paseto *paseto.V2 - symmetricKey []byte -} - -// NewPasetoMaker creates a new PasetoMaker -func NewPasetoMaker() error { - symmetricKey := config.File.JWT.SigningKey - if len(symmetricKey) != chacha20poly1305.KeySize { - return fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize) - } - - engine = &PasetoMaker{ - paseto: paseto.NewV2(), - symmetricKey: []byte(symmetricKey), - } - - return nil -} - -// CreateToken creates a new token for a specific username and duration -func (maker *PasetoMaker) CreateToken(id string, username string, duration time.Duration) (string, *Payload, error) { - payload, err := NewPayload(id, username, duration) - if err != nil { - return "", payload, err - } - - token, err := maker.paseto.Encrypt(maker.symmetricKey, payload, nil) - return token, payload, err -} - -// VerifyToken checks if the token is valid or not -func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) { - payload := &Payload{} - - err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil) - if err != nil { - return nil, ErrInvalidToken - } - - err = payload.Valid() - if err != nil { - return nil, err - } - - return payload, nil -} diff --git a/internal/pkg/token/payload.go b/internal/pkg/token/payload.go deleted file mode 100644 index eb81a7b..0000000 --- a/internal/pkg/token/payload.go +++ /dev/null @@ -1,43 +0,0 @@ -package token - -import ( - "errors" - "time" - - tk "github.com/golang-jwt/jwt/v5" -) - -// Different types of error returned by the VerifyToken function -var ( - ErrInvalidToken = errors.New("token is invalid") - ErrExpiredToken = errors.New("token has expired") -) - -// Payload contains the payload data of the token -type Payload struct { - ID string `json:"id"` - Username string `json:"username"` - tk.RegisteredClaims // v5版本新加的方法 -} - -// NewPayload creates a new token payload with a specific username and duration -func NewPayload(id string, username string, duration time.Duration) (*Payload, error) { - payload := &Payload{ - id, - username, - tk.RegisteredClaims{ - ExpiresAt: tk.NewNumericDate(time.Now().Add(duration)), // 过期时间24小时 - IssuedAt: tk.NewNumericDate(time.Now()), // 签发时间 - NotBefore: tk.NewNumericDate(time.Now()), // 生效时间 - }, - } - return payload, nil -} - -// Valid checks if the token payload is valid or not -func (payload *Payload) Valid() error { - if time.Now().After(payload.ExpiresAt.Time) { - return ErrExpiredToken - } - return nil -} diff --git a/internal/pkg/tpl/html.go b/internal/pkg/tpl/html.go deleted file mode 100644 index 8b3eff7..0000000 --- a/internal/pkg/tpl/html.go +++ /dev/null @@ -1,48 +0,0 @@ -package tpl - -import ( - "bytes" - "net/http" - "path/filepath" - "strings" - - "management/internal/erpserver/model/dto" -) - -type TemplateConfig struct { - Root string - Extension string - Layout string - Partial string -} - -type HtmlData struct { - IsAuthenticated bool - AuthorizeUser dto.AuthorizeUser - AuthorizeMenus []*dto.OwnerMenuDto - Data any -} - -func (r *render) HTML(w http.ResponseWriter, req *http.Request, tpl string, data map[string]any) { - name := strings.ReplaceAll(tpl, "/", "_") - t, ok := r.templates[name] - if !ok { - http.Error(w, "template is empty", http.StatusInternalServerError) - return - } - - hd := r.setDefaultData(req, data) - - buf := new(bytes.Buffer) - err := t.ExecuteTemplate(buf, filepath.Base(tpl), hd) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - _, err = buf.WriteTo(w) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} diff --git a/internal/pkg/tpl/render.go b/internal/pkg/tpl/render.go deleted file mode 100644 index 4934df0..0000000 --- a/internal/pkg/tpl/render.go +++ /dev/null @@ -1,46 +0,0 @@ -package tpl - -import ( - "html/template" - "net/http" - - v1 "management/internal/erpserver/service/v1" - "management/internal/pkg/session" -) - -type Renderer interface { - HTML(w http.ResponseWriter, req *http.Request, name string, data map[string]any) - JSON(w http.ResponseWriter, data any) - JSONF(w http.ResponseWriter, success bool, message string) - JSONOK(w http.ResponseWriter, message string) - JSONERR(w http.ResponseWriter, message string) -} - -type render struct { - session session.Session - config *TemplateConfig - templates map[string]*template.Template - - menusvc v1.MenuService -} - -func New(session session.Session, menusvc v1.MenuService) (Renderer, error) { - render := &render{ - session: session, - menusvc: menusvc, - config: &TemplateConfig{ - Root: ".", - Extension: ".tmpl", - Layout: "base", - Partial: "partial", - }, - } - - templates, err := render.createTemplateCache() - if err != nil { - return nil, err - } - - render.templates = templates - return render, nil -} diff --git a/internal/pkg/tpl/util.go b/internal/pkg/tpl/util.go deleted file mode 100644 index c2c630a..0000000 --- a/internal/pkg/tpl/util.go +++ /dev/null @@ -1,180 +0,0 @@ -package tpl - -import ( - "context" - "encoding/json" - "fmt" - "html/template" - "io/fs" - "net/http" - "os" - "path/filepath" - "slices" - "strings" - - "management/internal/erpserver/model/dto" - "management/internal/pkg/know" - templates "management/web/templates/manage" - - "github.com/justinas/nosurf" -) - -func (r *render) setDefaultData(req *http.Request, data map[string]any) map[string]any { - if data == nil { - data = make(map[string]any) - } - - ctx := req.Context() - isAuth := r.session.Exists(ctx, know.StoreName) - data["IsAuthenticated"] = isAuth - if isAuth { - var authUser dto.AuthorizeUser - u := r.session.GetBytes(ctx, know.StoreName) - _ = json.Unmarshal(u, &authUser) - - data["AuthorizeMenus"] = r.getCurrentPathBtns(ctx, authUser.RoleID, req.URL.Path) - } - token := nosurf.Token(req) - data["CsrfToken"] = token - data["CsrfTokenField"] = template.HTML(fmt.Sprintf(``, token)) - - return data -} - -func (r *render) getCurrentPathBtns(ctx context.Context, roleID int32, path string) []*dto.OwnerMenuDto { - var res []*dto.OwnerMenuDto - - // 获取当前登陆角色的权限 - menus, err := r.menusvc.ListByRoleIDToMap(ctx, roleID) - if err != nil { - return res - } - - menu, ok := menus[path] - if !ok { - return res - } - - for _, item := range menus { - 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 -} - -func (r *render) createTemplateCache() (map[string]*template.Template, error) { - cache := make(map[string]*template.Template) - pages, err := getFiles(r.config.Root, r.config.Extension) - if err != nil { - return nil, err - } - - layoutAndPartial, err := r.getLayoutAndPartials() - if err != nil { - return nil, err - } - - for _, page := range pages { - if strings.HasPrefix(page, "base") || strings.HasSuffix(page, "partial") { - continue - } - - name := filepath.Base(page) - pathArr := strings.Split(page, "/") - dir := pathArr[len(pathArr)-2 : len(pathArr)-1] - templateName := fmt.Sprintf("%s_%s", dir[0], name) - ts := template.Must(template.New(templateName).Funcs(r.btnFuncs()).Funcs(r.Methods()).ParseFS(templates.TemplateFS, page)) - if err != nil { - return nil, err - } - - ts, err = ts.ParseFS(templates.TemplateFS, layoutAndPartial...) - if err != nil { - return nil, err - } - - cache[templateName] = ts - } - - return cache, nil -} - -func (r *render) getLayoutAndPartials() ([]string, error) { - layouts, err := getFiles(r.config.Layout, r.config.Extension) - if err != nil { - return nil, err - } - - partials, err := getFiles(r.config.Partial, r.config.Extension) - if err != nil { - return nil, err - } - - return slices.Concat(layouts, partials), nil -} - -func getFiles(path string, stuffix string) ([]string, error) { - files := make([]string, 0) - b, err := pathExists(templates.TemplateFS, path) - if err != nil { - return nil, err - } - - if !b { - return files, nil - } - - err = fs.WalkDir(templates.TemplateFS, path, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - return nil - } - if strings.HasSuffix(path, stuffix) { - files = append(files, path) - } - return nil - }) - // err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { - // if info == nil { - // return err - // } - // if info.IsDir() { - // return nil - // } - // // 将模板后缀的文件放到列表 - // if strings.HasSuffix(path, stuffix) { - // files = append(files, path) - // } - // return nil - // }) - return files, err -} - -func pathExists(fs fs.FS, path string) (bool, error) { - _, err := fs.Open(path) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - return true, err -} - -func firstLower(s string) string { - if len(s) == 0 { - return s - } - - return strings.ToLower(s[:1]) + s[1:] -} diff --git a/internal/pkg/validation/validator.go b/internal/pkg/validation/validator.go index cf22dc9..220ee21 100644 --- a/internal/pkg/validation/validator.go +++ b/internal/pkg/validation/validator.go @@ -13,15 +13,15 @@ import ( // 初始化一个验证器实例 var validate = validator.New() -// 自定义验证规则:requiredint +// 自定义验证规则 func init() { - validate.RegisterValidation("telephone", func(fl validator.FieldLevel) bool { + _ = validate.RegisterValidation("telephone", func(fl validator.FieldLevel) bool { if err := IsValidPhone(fl.Field().String()); err != nil { return false } return true }) - validate.RegisterValidation("dateonly", func(fl validator.FieldLevel) bool { + _ = validate.RegisterValidation("dateonly", func(fl validator.FieldLevel) bool { _, err := time.ParseInLocation("2006-01-02", fl.Field().String(), time.Local) if err != nil { return false @@ -33,7 +33,8 @@ func init() { func ValidateForm(s any) error { // 验证结构体数据 if err := validate.Struct(s); err != nil { - if _, ok := err.(*validator.InvalidValidationError); ok { + var invalidValidationError *validator.InvalidValidationError + if errors.As(err, &invalidValidationError) { return errors.New("验证器配置错误") } diff --git a/main.go b/main.go index 0b74cfe..3c1c2d4 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,6 @@ package main -import ( - "management/cmd" -) +import "management/cmd" func main() { cmd.Execute() diff --git a/web/templates/manage/budget/edit.tmpl b/web/templates/manage/budget/edit.tmpl deleted file mode 100644 index 669f5e9..0000000 --- a/web/templates/manage/budget/edit.tmpl +++ /dev/null @@ -1,285 +0,0 @@ -{{template "header"}} - -
- {{.CsrfTokenField}} - - -
-
-
-
项目信息
-
-
-
选择项目
-
-
-
-
- -
-
项目预算信息
-
- {{if .Item.ID}} -
-
编号
-
- {{.Item.ID}} -
-
- {{end}} -
- -
- -
-
-
- -
- -
-
-
- -
-
-
- -
- -
- -
- -
-
-
- -
- -
- -
- -
- -
- -
-
-
- -
- -
-
-
-
- -
-
其他
-
-
- -
- -
-
- {{if .Item.ID}} -
-
创建
-
- {{.Item.CreatedName}} - ({{dateFormat .Item.CreatedAt}}) -
-
-
-
更新
-
- {{.Item.UpdatedName}} - ({{dateFormat .Item.UpdatedAt}}) -
-
- {{end}} -
-
-
-
- -
-
- {{ submitBtn .AuthorizeMenus "save"}} - -
-
- -
- -{{define "css"}} - -{{end}} - -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/budget/list.tmpl b/web/templates/manage/budget/list.tmpl deleted file mode 100644 index f97d742..0000000 --- a/web/templates/manage/budget/list.tmpl +++ /dev/null @@ -1,276 +0,0 @@ -{{template "header" .}} - - - - - - -
-
-
- - - -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/cache/list.tmpl b/web/templates/manage/cache/list.tmpl deleted file mode 100644 index 7079a43..0000000 --- a/web/templates/manage/cache/list.tmpl +++ /dev/null @@ -1,186 +0,0 @@ -{{template "header"}} - -
-
-
-
查询
-
-
-
-
- {{.CsrfTokenField}} -
-
- -
-
- - -
-
-
-
-
-
-
-
-
- - - - -
-
-
-
-
- - - -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/customer/edit.tmpl b/web/templates/manage/customer/edit.tmpl deleted file mode 100644 index 566d6d1..0000000 --- a/web/templates/manage/customer/edit.tmpl +++ /dev/null @@ -1,184 +0,0 @@ -{{template "header"}} - -
-
-
- {{.CsrfTokenField}} - - -
-
    -
  • 基础信息
  • -
  • 其它
  • -
- -
-
- {{if .Item.ID}} -
-
ID
-
- {{.Item.ID}} -
-
- {{end}} -
- -
- -
-
-
- -
- -
- -
- -
-
- -
- -
- -
- - -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- {{if .Item.ID}} -
-
创建时间/人
-
- {{dateFormat .Item.CreatedAt}} / {{.CreatedName}} -
-
-
-
更新时间/人
-
- {{dateFormat .Item.UpdatedAt}} / {{.UpdatedName}} -
-
- {{end}} -
-
-
- -
-
- {{ submitBtn .AuthorizeMenus "save"}} - -
-
- -
-
-
- -{{define "css"}} - -{{end}} - -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/customer/list.tmpl b/web/templates/manage/customer/list.tmpl deleted file mode 100644 index 238cb74..0000000 --- a/web/templates/manage/customer/list.tmpl +++ /dev/null @@ -1,350 +0,0 @@ -{{template "header" .}} - - - - - - - - - - -
-
-
- -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/expense/edit.tmpl b/web/templates/manage/expense/edit.tmpl deleted file mode 100644 index 4786b18..0000000 --- a/web/templates/manage/expense/edit.tmpl +++ /dev/null @@ -1,269 +0,0 @@ -{{template "header"}} - -
- {{.CsrfTokenField}} - - -
-
-
-
项目信息
-
-
-
选择项目
-
-
-
-
- -
-
预算信息
-
-
-
选择预算
-
-
-
-
- -
-
报销信息
-
- {{if .Item.ID}} -
-
编号
-
- {{.Item.ID}} -
-
- {{end}} -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
其他
-
-
- -
- -
-
- {{if .Item.ID}} -
-
创建
-
- {{.Item.CreatedName}} - ({{dateFormat .Item.CreatedAt}}) -
-
-
-
更新
-
- {{.Item.UpdatedName}} - ({{dateFormat .Item.UpdatedAt}}) -
-
- {{end}} -
-
-
-
- -
-
- {{ submitBtn .AuthorizeMenus "save"}} - -
-
- -
- -{{define "css"}} - -{{end}} - -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/expense/list.tmpl b/web/templates/manage/expense/list.tmpl deleted file mode 100644 index 0ab560e..0000000 --- a/web/templates/manage/expense/list.tmpl +++ /dev/null @@ -1,231 +0,0 @@ -{{template "header" .}} - - - - - - - - -
-
-
- -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/home/home.tmpl b/web/templates/manage/home/home.tmpl index 8d466af..31cfd28 100644 --- a/web/templates/manage/home/home.tmpl +++ b/web/templates/manage/home/home.tmpl @@ -80,7 +80,7 @@ -
+
@@ -103,9 +103,9 @@ -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/income/list.tmpl b/web/templates/manage/income/list.tmpl deleted file mode 100644 index 26a778f..0000000 --- a/web/templates/manage/income/list.tmpl +++ /dev/null @@ -1,254 +0,0 @@ -{{template "header" .}} - - - - - - - -
-
-
- -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/project/dashboard.tmpl b/web/templates/manage/project/dashboard.tmpl deleted file mode 100644 index bad2064..0000000 --- a/web/templates/manage/project/dashboard.tmpl +++ /dev/null @@ -1,423 +0,0 @@ -{{template "header" .}} - -
-
-
- {{.CsrfTokenField}} -
-
-
- -
-
-
- -
- -
-
-
- -
-
-
-
-
- -
-
-
-
项目收入
-
-
-
-
- - - - - - - - - - -
-
-
-
-
-
-
-
项目支出
-
-
-
-
- - - - - - - - - - - - - - -
-
-
-
-
-
-
-
项目利润
-
-
-
-
- - - - - - - - - - - -
-
-
-
-
-
-
-
项目利润率
-
-
-
-
- - - - - - - - - - - - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
收入列表
-
-
-
-
-
-
- -
-
-
-
支出列表
-
-
-
-
-
-
- -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/project/edit.tmpl b/web/templates/manage/project/edit.tmpl deleted file mode 100644 index fb233cd..0000000 --- a/web/templates/manage/project/edit.tmpl +++ /dev/null @@ -1,516 +0,0 @@ -{{template "header"}} - -
-
- {{.CsrfTokenField}} - - -
-
- -
-
客户信息
-
-
-
选择客户
-
- -
-
客户编号
-
- -
-
客户名称
-
- -
-
-
-
- -
-
项目信息
-
- {{if .Item.ID}} -
-
编号
-
- {{.Item.ID}} -
-
- {{end}} -
- -
- -
-
-
- -
- {{range .Statuses}} - {{if.Selected}} - - {{else}} - - {{end}} - {{end}} -
-
-
- -
- -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- {{template "upload_mutil" .Item.ProjectFiles}} -
-
-
-
- -
-
项目团队
-
-
-
项目经理
-
-
项目成员
-
-
-
-
- -
-
申请信息
-
-
-
申请人
-
- -
- -
-
-
-
- -
-
其他
-
- {{if .Item.ID}} -
-
创建
-
- {{.Item.CreatedName}} - ({{dateFormat .Item.CreatedAt}}) -
-
-
-
更新
-
- {{.Item.UpdatedName}} - ({{dateFormat .Item.UpdatedAt}}) -
-
- {{end}} -
-
-
-
- -
-
- {{ submitBtn .AuthorizeMenus "save"}} - -
-
- -
-
- -{{define "css"}} - -{{end}} - -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/project/list.tmpl b/web/templates/manage/project/list.tmpl deleted file mode 100644 index 2d7a508..0000000 --- a/web/templates/manage/project/list.tmpl +++ /dev/null @@ -1,262 +0,0 @@ -{{template "header" .}} - - - - - - - - - -
-
-
- -{{define "js"}} - -{{end}} - -{{template "footer" .}} \ No newline at end of file diff --git a/web/templates/manage/system/category/edit.tmpl b/web/templates/manage/system/category/edit.tmpl index d6bdb57..85819ec 100644 --- a/web/templates/manage/system/category/edit.tmpl +++ b/web/templates/manage/system/category/edit.tmpl @@ -187,7 +187,7 @@ function getCategory() { $.ajax({ - url: "/system/category/data?type=xmselect_tree", + url: "/system/category/data?type=xm_select_tree", type: 'post', dataType: 'json', headers: { 'X-CSRF-Token': $('#csrf_token').val() }, diff --git a/web/templates/manage/system/config/list.tmpl b/web/templates/manage/system/config/list.tmpl index ecb8534..4471d2f 100644 --- a/web/templates/manage/system/config/list.tmpl +++ b/web/templates/manage/system/config/list.tmpl @@ -9,7 +9,7 @@