diff --git a/cmd/erp.go b/cmd/erp.go index e1c8c61..7ed2b28 100644 --- a/cmd/erp.go +++ b/cmd/erp.go @@ -65,7 +65,7 @@ func runErp(ctx context.Context) error { rander, err := tpl.New(session, biz.SystemV1().MenuBiz()) checkError(err) - handler := handler.NewHandler(conf, rander, redis, session, biz) + handler := handler.NewHandler(conf, rander, redis, session, biz, middleware) address := fmt.Sprintf("%s:%d", conf.App.Host, conf.App.Port) log.Printf("Starting erp manage server on %s", address) diff --git a/cmd/manage.go b/cmd/manage.go deleted file mode 100644 index 6e15cd0..0000000 --- a/cmd/manage.go +++ /dev/null @@ -1,54 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "log" - - "management/internal/config" - "management/internal/pkg/logger" - "management/internal/pkg/snowflake" - - dbinit "management/internal/db/init" - db "management/internal/db/sqlc" - - router "management/internal/router/manage" - - "github.com/spf13/cobra" -) - -var manageCmd = &cobra.Command{ - Use: "manage", - Short: "Start management server", - Long: `A Service to management manage`, - Run: func(cmd *cobra.Command, args []string) { - err := runManage(cmd.Context()) - if err != nil { - log.Fatalf("run manage failed: %v", err) - } - }, -} - -func init() { - manageCmd.Flags().StringVarP(&configPath, "config", "c", "", "Custom config file path") - rootCmd.AddCommand(manageCmd) -} - -func runManage(ctx context.Context) error { - mustInitAny(configPath, config.Init) - logger.Init() - mustInitAny(ctx, db.NewStore) - // 初始化数据 - dbinit.InitSeed() - // mustInit(redis.Init) - // session.Init() - mustInit(snowflake.Init) - // mustInit(token.NewPasetoMaker) - // mustInit(tencentoss.Init) - // mustInit(tpl.Init) - - address := fmt.Sprintf("%s:%d", config.File.App.Host, config.File.App.Port) - log.Printf("Starting manage server on %s", address) - server := InitServer(address, router.NewRouter()) - return server.ListenAndServe() -} diff --git a/go.mod b/go.mod index 254bd39..2875fa2 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/aead/chacha20poly1305 v0.0.0-20201124145622-1a5aba2a8b29 github.com/alexedwards/scs/pgxstore v0.0.0-20250212122300-421ef1d8611c github.com/alexedwards/scs/v2 v2.8.0 - github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/bwmarrin/snowflake v0.3.0 github.com/fsnotify/fsnotify v1.8.0 github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 @@ -14,25 +13,24 @@ require ( github.com/go-chi/chi/v5 v5.2.1 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 - github.com/go-playground/validator/v10 v10.25.0 + github.com/go-playground/validator/v10 v10.26.0 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/uuid v1.6.0 github.com/gorilla/schema v1.4.1 github.com/h2non/filetype v1.1.3 github.com/hirochachacha/go-smb2 v1.1.0 github.com/jackc/pgconn v1.14.3 - github.com/jackc/pgx/v5 v5.7.2 + github.com/jackc/pgx/v5 v5.7.4 github.com/justinas/nosurf v1.1.1 github.com/matoous/go-nanoid/v2 v2.1.0 github.com/mojocn/base64Captcha v1.3.8 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/o1egl/paseto v1.0.0 - github.com/redis/go-redis/v9 v9.7.1 - github.com/rs/zerolog v1.33.0 + github.com/redis/go-redis/v9 v9.7.3 + github.com/rs/zerolog v1.34.0 github.com/spf13/cobra v1.9.1 - github.com/spf13/viper v1.20.0 + github.com/spf13/viper v1.20.1 github.com/sqids/sqids-go v0.4.1 - github.com/tencentyun/cos-go-sdk-v5 v0.7.61 github.com/zhang2092/browser v0.0.2 golang.org/x/crypto v0.36.0 ) @@ -45,7 +43,6 @@ require ( github.com/bytedance/sonic v1.12.9 // indirect github.com/bytedance/sonic/loader v0.2.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/clbanning/mxj v1.8.4 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect @@ -54,7 +51,6 @@ 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/go-querystring v1.1.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 @@ -67,13 +63,11 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mozillazg/go-httpheader v0.4.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sagikazarmark/locafero v0.8.0 // indirect + github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.7.1 // indirect @@ -84,11 +78,10 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.14.0 // indirect golang.org/x/image v0.25.0 // indirect - golang.org/x/net v0.37.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.10.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 2ad01ef..e9390b1 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU= @@ -12,8 +11,6 @@ github.com/alexedwards/scs/pgxstore v0.0.0-20250212122300-421ef1d8611c h1:Y33ELO github.com/alexedwards/scs/pgxstore v0.0.0-20250212122300-421ef1d8611c/go.mod h1:hwveArYcjyOK66EViVgVU5Iqj7zyEsWjKXMQhDJrTLI= github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw= github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= -github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= -github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= @@ -29,8 +26,6 @@ github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wio github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= @@ -64,8 +59,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= -github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= @@ -75,14 +70,9 @@ 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.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= @@ -112,8 +102,8 @@ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= -github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= @@ -147,9 +137,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -157,9 +144,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg= github.com/mojocn/base64Captcha v1.3.8/go.mod h1:QFZy927L8HVP3+VV5z2b1EAEiv1KxVJKZbAucVgLUy4= -github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= -github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w= -github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/o1egl/paseto v1.0.0 h1:bwpvPu2au176w4IBlhbyUv/S5VPptERIA99Oap5qUd0= @@ -171,17 +155,17 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc= -github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.8.0 h1:mXaMVw7IqxNBxfv3LdWt9MDmcWDQ1fagDH918lOdVaQ= -github.com/sagikazarmark/locafero v0.8.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= +github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= +github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= @@ -196,8 +180,8 @@ github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= -github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw= github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -216,10 +200,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= -github.com/tencentyun/cos-go-sdk-v5 v0.7.61 h1:tKNIjvsezkdtajqE887XAw1VL8Pq1HNtpc7rfgz25lA= -github.com/tencentyun/cos-go-sdk-v5 v0.7.61/go.mod h1:8+hG+mQMuRP/OIS9d83syAvXvrMj9HhkND6Q1fLghw0= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -258,8 +238,8 @@ 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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -306,8 +286,6 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -315,7 +293,6 @@ 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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/db/init/config.go b/internal/db/init/config.go index 8c6177a..8e236ea 100644 --- a/internal/db/init/config.go +++ b/internal/db/init/config.go @@ -5,7 +5,7 @@ import ( "encoding/json" db "management/internal/db/sqlc" - "management/internal/global/pearadmin" + "management/internal/pkg/know/pearadmin" ) func initConfig() error { diff --git a/internal/db/init/init.go b/internal/db/init/init.go index 1a14c19..38f0b39 100644 --- a/internal/db/init/init.go +++ b/internal/db/init/init.go @@ -5,7 +5,7 @@ import ( "log" db "management/internal/db/sqlc" - "management/internal/global/pearadmin" + "management/internal/pkg/know/pearadmin" ) func InitSeed() { diff --git a/internal/erpserver/biz/biz.go b/internal/erpserver/biz/biz.go index 8acad7f..c3d20b3 100644 --- a/internal/erpserver/biz/biz.go +++ b/internal/erpserver/biz/biz.go @@ -2,8 +2,11 @@ package biz import ( db "management/internal/db/sqlc" + budgetv1 "management/internal/erpserver/biz/v1/budget" commonv1 "management/internal/erpserver/biz/v1/common" customerv1 "management/internal/erpserver/biz/v1/customer" + expensev1 "management/internal/erpserver/biz/v1/expense" + incomev1 "management/internal/erpserver/biz/v1/income" projectv1 "management/internal/erpserver/biz/v1/project" systemv1 "management/internal/erpserver/biz/v1/system" "management/internal/pkg/redis" @@ -18,8 +21,14 @@ type IBiz interface { SystemV1() systemv1.SystemBiz // 项目管理 ProjectV1() projectv1.ProjectBiz + // 预算管理 + BudgetV1() budgetv1.BudgetBiz // 客户管理 CustomerV1() customerv1.CustomerBiz + // 收入管理 + IncomeV1() incomev1.IncomeBiz + // 支出管理 + ExpenseV1() expensev1.ExpenseBiz } // biz 是 IBiz 的一个具体实现. @@ -55,6 +64,18 @@ func (b *biz) ProjectV1() projectv1.ProjectBiz { return projectv1.New(b.store, b.redis) } +func (b *biz) BudgetV1() budgetv1.BudgetBiz { + return budgetv1.New(b.store, b.redis) +} + func (b *biz) CustomerV1() customerv1.CustomerBiz { return customerv1.New(b.store, b.redis) } + +func (b *biz) IncomeV1() incomev1.IncomeBiz { + return incomev1.New(b.store, b.redis) +} + +func (b *biz) ExpenseV1() expensev1.ExpenseBiz { + return expensev1.New(b.store, b.redis) +} diff --git a/internal/erpserver/biz/v1/budget/budget.go b/internal/erpserver/biz/v1/budget/budget.go new file mode 100644 index 0000000..c26982a --- /dev/null +++ b/internal/erpserver/biz/v1/budget/budget.go @@ -0,0 +1,45 @@ +package budget + +import ( + "context" + + db "management/internal/db/sqlc" + "management/internal/pkg/redis" +) + +type BudgetBiz interface { + Create(ctx context.Context, arg *db.CreateBudgetParams) (*db.Budget, error) + Update(ctx context.Context, arg *db.UpdateBudgetParams) (*db.Budget, error) + List(ctx context.Context, arg *db.ListBudgetConditionParam) ([]*db.BudgetView, int64, error) + Get(ctx context.Context, id int64) (*db.Budget, error) +} + +type budgetBiz struct { + store db.Store + redis redis.IRedis +} + +var _ BudgetBiz = (*budgetBiz)(nil) + +func New(store db.Store, redis redis.IRedis) *budgetBiz { + return &budgetBiz{ + store: store, + redis: redis, + } +} + +func (b *budgetBiz) Create(ctx context.Context, arg *db.CreateBudgetParams) (*db.Budget, error) { + return b.store.CreateBudget(ctx, arg) +} + +func (b *budgetBiz) Update(ctx context.Context, arg *db.UpdateBudgetParams) (*db.Budget, error) { + return b.store.UpdateBudget(ctx, arg) +} + +func (b *budgetBiz) List(ctx context.Context, arg *db.ListBudgetConditionParam) ([]*db.BudgetView, int64, error) { + return b.store.ListBudgetCondition(ctx, arg) +} + +func (b *budgetBiz) Get(ctx context.Context, id int64) (*db.Budget, error) { + return b.store.GetBudget(ctx, id) +} diff --git a/internal/erpserver/biz/v1/expense/expsense.go b/internal/erpserver/biz/v1/expense/expsense.go new file mode 100644 index 0000000..cfa97a3 --- /dev/null +++ b/internal/erpserver/biz/v1/expense/expsense.go @@ -0,0 +1,67 @@ +package expense + +import ( + "context" + + db "management/internal/db/sqlc" + "management/internal/pkg/redis" + + "github.com/jackc/pgx/v5/pgtype" +) + +type ExpenseBiz interface { + Create(ctx context.Context, arg *db.CreateExpenseParams) (*db.Expense, error) + Update(ctx context.Context, arg *db.UpdateExpenseParams) (*db.Expense, error) + List(ctx context.Context, arg *db.ListExpenseConditionParam) ([]*db.ExpenseView, int64, error) + Get(ctx context.Context, id int64) (*db.Expense, error) + Sum(ctx context.Context) (pgtype.Numeric, error) + SumByProjectID(ctx context.Context, id int64) (pgtype.Numeric, error) + Statistics(ctx context.Context) ([]*db.StatisticsExpenseRow, error) + StatisticsByProjectID(ctx context.Context, projectID int64) ([]*db.StatisticsExpenseByProjectIDRow, error) +} + +type expenseBiz struct { + store db.Store + redis redis.IRedis +} + +var _ ExpenseBiz = (*expenseBiz)(nil) + +func New(store db.Store, redis redis.IRedis) *expenseBiz { + return &expenseBiz{ + store: store, + redis: redis, + } +} + +func (b *expenseBiz) Create(ctx context.Context, arg *db.CreateExpenseParams) (*db.Expense, error) { + return b.store.CreateExpense(ctx, arg) +} + +func (b *expenseBiz) Update(ctx context.Context, arg *db.UpdateExpenseParams) (*db.Expense, error) { + return b.store.UpdateExpense(ctx, arg) +} + +func (b *expenseBiz) List(ctx context.Context, arg *db.ListExpenseConditionParam) ([]*db.ExpenseView, int64, error) { + return b.store.ListExpenseCondition(ctx, arg) +} + +func (b *expenseBiz) Get(ctx context.Context, id int64) (*db.Expense, error) { + return b.store.GetExpense(ctx, id) +} + +func (b *expenseBiz) Sum(ctx context.Context) (pgtype.Numeric, error) { + return b.store.SumExpense(ctx) +} + +func (b *expenseBiz) SumByProjectID(ctx context.Context, id int64) (pgtype.Numeric, error) { + return b.store.SumExpenseByProjectID(ctx, id) +} + +func (b *expenseBiz) Statistics(ctx context.Context) ([]*db.StatisticsExpenseRow, error) { + return b.store.StatisticsExpense(ctx) +} + +func (b *expenseBiz) StatisticsByProjectID(ctx context.Context, projectID int64) ([]*db.StatisticsExpenseByProjectIDRow, error) { + return b.store.StatisticsExpenseByProjectID(ctx, projectID) +} diff --git a/internal/erpserver/biz/v1/income/income.go b/internal/erpserver/biz/v1/income/income.go new file mode 100644 index 0000000..fac15be --- /dev/null +++ b/internal/erpserver/biz/v1/income/income.go @@ -0,0 +1,67 @@ +package income + +import ( + "context" + + db "management/internal/db/sqlc" + "management/internal/pkg/redis" + + "github.com/jackc/pgx/v5/pgtype" +) + +type IncomeBiz interface { + Create(ctx context.Context, arg *db.CreateIncomeParams) (*db.Income, error) + Update(ctx context.Context, arg *db.UpdateIncomeParams) (*db.Income, error) + List(ctx context.Context, arg *db.ListIncomeConditionParam) ([]*db.IncomeView, int64, error) + Get(ctx context.Context, id int64) (*db.Income, error) + Sum(ctx context.Context) (pgtype.Numeric, error) + SumByProjectID(ctx context.Context, id int64) (pgtype.Numeric, error) + Statistics(ctx context.Context) ([]*db.StatisticsIncomeRow, error) + StatisticsByProjectID(ctx context.Context, projectID int64) ([]*db.StatisticsIncomeByProjectIDRow, error) +} + +type incomeBiz struct { + store db.Store + redis redis.IRedis +} + +var _ IncomeBiz = (*incomeBiz)(nil) + +func New(store db.Store, redis redis.IRedis) *incomeBiz { + return &incomeBiz{ + store: store, + redis: redis, + } +} + +func (b *incomeBiz) Create(ctx context.Context, arg *db.CreateIncomeParams) (*db.Income, error) { + return b.store.CreateIncome(ctx, arg) +} + +func (b *incomeBiz) Update(ctx context.Context, arg *db.UpdateIncomeParams) (*db.Income, error) { + return b.store.UpdateIncome(ctx, arg) +} + +func (b *incomeBiz) List(ctx context.Context, arg *db.ListIncomeConditionParam) ([]*db.IncomeView, int64, error) { + return b.store.ListIncomeCondition(ctx, arg) +} + +func (b *incomeBiz) Get(ctx context.Context, id int64) (*db.Income, error) { + return b.store.GetIncome(ctx, id) +} + +func (b *incomeBiz) Sum(ctx context.Context) (pgtype.Numeric, error) { + return b.store.SumIncome(ctx) +} + +func (b *incomeBiz) SumByProjectID(ctx context.Context, id int64) (pgtype.Numeric, error) { + return b.store.SumIncomeByProjectID(ctx, id) +} + +func (b *incomeBiz) Statistics(ctx context.Context) ([]*db.StatisticsIncomeRow, error) { + return b.store.StatisticsIncome(ctx) +} + +func (b *incomeBiz) StatisticsByProjectID(ctx context.Context, projectID int64) ([]*db.StatisticsIncomeByProjectIDRow, error) { + return b.store.StatisticsIncomeByProjectID(ctx, projectID) +} diff --git a/internal/erpserver/biz/v1/project/project.go b/internal/erpserver/biz/v1/project/project.go index d521c4b..fd475df 100644 --- a/internal/erpserver/biz/v1/project/project.go +++ b/internal/erpserver/biz/v1/project/project.go @@ -2,9 +2,12 @@ package project import ( "context" + "strconv" db "management/internal/db/sqlc" + "management/internal/erpserver/model/view" "management/internal/pkg/redis" + "management/internal/pkg/tpl/html" ) type ProjectBiz interface { @@ -13,11 +16,17 @@ type ProjectBiz interface { List(ctx context.Context, q *db.ListProjectConditionParam) ([]*db.ProjectView, int64, error) Get(ctx context.Context, id int64) (*db.Project, error) + XmSelect(ctx context.Context) ([]*view.XmSelect, error) + ListHtml(ctx context.Context) ([]*html.SelectDict, error) + ProjectExpansion } type ProjectExpansion interface { ListProjectFiles(ctx context.Context, projectID int64) ([]*db.ProjectFile, error) + + Statistics(ctx context.Context) ([]*db.StatisticsProjectsRow, error) + StatisticsItem(ctx context.Context, id int64) (*db.StatisticsProjectItemRow, error) } type projectBiz struct { @@ -91,3 +100,49 @@ func (b *projectBiz) Get(ctx context.Context, id int64) (*db.Project, error) { func (b *projectBiz) ListProjectFiles(ctx context.Context, projectID int64) ([]*db.ProjectFile, error) { return b.store.ListProjectFiles(ctx, projectID) } + +func (b *projectBiz) XmSelect(ctx context.Context) ([]*view.XmSelect, error) { + all, err := b.store.AllProjects(ctx) + if err != nil || len(all) == 0 { + return nil, err + } + + var res []*view.XmSelect + for _, item := range all { + res = append(res, &view.XmSelect{ + Name: item.Name, + Value: strconv.FormatInt(item.ID, 10), + }) + } + return res, nil +} + +func (b *projectBiz) ListHtml(ctx context.Context) ([]*html.SelectDict, error) { + all, err := b.store.AllProjects(ctx) + if err != nil || len(all) == 0 { + return nil, err + } + + var res []*html.SelectDict + res = append(res, &html.SelectDict{ + Name: "请选择", + Value: "0", + }) + for _, v := range all { + item := html.SelectDict{ + Name: v.Name, + Value: strconv.Itoa(int(v.ID)), + } + res = append(res, &item) + } + + return res, nil +} + +func (b *projectBiz) Statistics(ctx context.Context) ([]*db.StatisticsProjectsRow, error) { + return b.store.StatisticsProjects(ctx) +} + +func (b *projectBiz) StatisticsItem(ctx context.Context, id int64) (*db.StatisticsProjectItemRow, error) { + return b.store.StatisticsProjectItem(ctx, id) +} diff --git a/internal/erpserver/biz/v1/system/category.go b/internal/erpserver/biz/v1/system/category.go index 26b44ca..571644e 100644 --- a/internal/erpserver/biz/v1/system/category.go +++ b/internal/erpserver/biz/v1/system/category.go @@ -10,7 +10,7 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" "management/internal/erpserver/model/view" - "management/internal/global/keys" + "management/internal/pkg/know" "management/internal/pkg/redis" "management/internal/pkg/tpl/html" ) @@ -25,6 +25,7 @@ type CategoryBiz interface { RebuildParentPath(ctx context.Context) error Tree(ctx context.Context, id int32) ([]*view.LayuiTree, error) + XmSelect(ctx context.Context, letter string) ([]*view.XmSelect, error) XmSelectTree(ctx context.Context, id int32) ([]*view.XmSelectTree, error) ListHtmlByLetter(ctx context.Context, letter string) ([]*html.SelectDict, error) @@ -45,7 +46,7 @@ func NewCategory(store db.Store, redis redis.IRedis) *categoryBiz { } func (b *categoryBiz) All(ctx context.Context) ([]*db.Category, error) { - key := keys.GetManageKey(ctx, keys.AllCategories) + key := know.GetManageKey(ctx, know.AllCategories) bs, err := redis.GetBytes(ctx, key) if err == nil { var res []*db.Category @@ -115,8 +116,8 @@ func (b *categoryBiz) Refresh(ctx context.Context) ([]*db.Category, error) { return nil, err } - redis.Del(ctx, keys.GetManageKey(ctx, keys.AllCategorySimple)) - key := keys.GetManageKey(ctx, keys.AllCategories) + redis.Del(ctx, know.GetManageKey(ctx, know.AllCategorySimple)) + key := know.GetManageKey(ctx, know.AllCategories) err = redis.Set(ctx, key, bs, time.Hour*6) return all, err } @@ -134,6 +135,36 @@ func (b *categoryBiz) Tree(ctx context.Context, id int32) ([]*view.LayuiTree, er return b.toTree(id, all), nil } +func (b *categoryBiz) XmSelect(ctx context.Context, letter string) ([]*view.XmSelect, error) { + all, err := b.All(ctx) + if err != nil || len(all) == 0 { + return nil, err + } + + var current *db.Category + for _, v := range all { + if v.Letter == letter { + current = v + break + } + } + if current == nil { + return nil, errors.New("未找到当前类别") + } + + var res []*view.XmSelect + for _, v := range all { + if v.ParentID == current.ID { + item := view.XmSelect{ + Name: v.Name, + Value: strconv.FormatInt(int64(v.ID), 10), + } + res = append(res, &item) + } + } + return res, nil +} + func (b *categoryBiz) XmSelectTree(ctx context.Context, id int32) ([]*view.XmSelectTree, error) { all, err := b.All(ctx) if err != nil { diff --git a/internal/erpserver/biz/v1/system/config.go b/internal/erpserver/biz/v1/system/config.go index 05411c0..b38a140 100644 --- a/internal/erpserver/biz/v1/system/config.go +++ b/internal/erpserver/biz/v1/system/config.go @@ -7,8 +7,8 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" - "management/internal/global/keys" - "management/internal/global/pearadmin" + "management/internal/pkg/know" + "management/internal/pkg/know/pearadmin" "management/internal/pkg/redis" ) @@ -71,7 +71,7 @@ func (b *configBiz) List(ctx context.Context, q dto.SearchDto) ([]*db.SysConfig, func (b *configBiz) Pear(ctx context.Context) (*dto.PearConfig, error) { // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.PearAdmin) + key := know.GetManageKey(ctx, know.PearAdmin) bs, err := b.redis.GetBytes(ctx, key) if err == nil { var res *dto.PearConfig diff --git a/internal/erpserver/biz/v1/system/department.go b/internal/erpserver/biz/v1/system/department.go index 28f384a..b17836f 100644 --- a/internal/erpserver/biz/v1/system/department.go +++ b/internal/erpserver/biz/v1/system/department.go @@ -9,7 +9,7 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" "management/internal/erpserver/model/view" - "management/internal/global/keys" + "management/internal/pkg/know" "management/internal/pkg/redis" ) @@ -45,7 +45,7 @@ func NewDepartment(store db.Store, redis redis.IRedis) *departmentBiz { } func (b *departmentBiz) All(ctx context.Context) ([]*db.SysDepartment, error) { - key := keys.GetManageKey(ctx, keys.AllDepartments) + key := know.GetManageKey(ctx, know.AllDepartments) bs, err := redis.GetBytes(ctx, key) if err == nil { var res []*db.SysDepartment @@ -115,7 +115,7 @@ func (b *departmentBiz) Refresh(ctx context.Context) ([]*db.SysDepartment, error return nil, err } - key := keys.GetManageKey(ctx, keys.AllDepartments) + key := know.GetManageKey(ctx, know.AllDepartments) err = redis.Set(ctx, key, bs, time.Hour*6) if err != nil { return nil, err diff --git a/internal/erpserver/biz/v1/system/menu.go b/internal/erpserver/biz/v1/system/menu.go index b3884c5..4d5fe58 100644 --- a/internal/erpserver/biz/v1/system/menu.go +++ b/internal/erpserver/biz/v1/system/menu.go @@ -11,7 +11,7 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" "management/internal/erpserver/model/view" - "management/internal/global/keys" + "management/internal/pkg/know" "management/internal/pkg/redis" ) @@ -71,7 +71,7 @@ func (b *menuBiz) GetSysMenuByUrl(ctx context.Context, url string) (*db.SysMenu, } func (b *menuBiz) AllMenusCache(ctx context.Context) ([]*db.SysMenu, error) { - key := keys.GetManageKey(ctx, keys.AllMenus) + key := know.GetManageKey(ctx, know.AllMenus) bs, err := b.redis.GetBytes(ctx, key) if err == nil { var res []*db.SysMenu @@ -105,7 +105,7 @@ func (b *menuBiz) ListMenuTree(ctx context.Context) ([]*db.SysMenuDto, error) { func (b *menuBiz) ListOwnerMenuByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error) { // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) + key := know.GetManageKey(ctx, know.OwnerMenus, roleID) bs, err := b.redis.GetBytes(ctx, key) if err == nil { var res []*dto.OwnerMenuDto @@ -141,14 +141,14 @@ func (b *menuBiz) SetOwnerListMenuByRoleID(ctx context.Context, roleID int32) ([ return nil, err } - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) + key := know.GetManageKey(ctx, know.OwnerMenus, roleID) _ = redis.Set(ctx, key, bs, time.Hour*6) return res, nil } func (b *menuBiz) RecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error) { // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.RecursiveMenus, roleID) + key := know.GetManageKey(ctx, know.RecursiveMenus, roleID) bs, err := b.redis.GetBytes(ctx, key) if err == nil { var res []*dto.MenuUIDto @@ -195,14 +195,14 @@ func (b *menuBiz) SetRecursiveSysMenus(ctx context.Context, roleID int32) ([]*dt return nil, err } - key := keys.GetManageKey(ctx, keys.RecursiveMenus, roleID) + key := know.GetManageKey(ctx, know.RecursiveMenus, roleID) _ = redis.Set(ctx, key, bs, time.Hour*6) return tree, nil } func (b *menuBiz) MapOwnerMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) { // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) + key := know.GetManageKey(ctx, know.OwnerMenus, roleID) bs, err := b.redis.GetBytes(ctx, key) if err == nil { var res map[string]*dto.OwnerMenuDto @@ -238,7 +238,7 @@ func (b *menuBiz) SetOwnerMapMenuByRoleID(ctx context.Context, roleID int32) (ma return nil, err } - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) + key := know.GetManageKey(ctx, know.OwnerMenus, roleID) _ = redis.Set(ctx, key, bs, time.Hour*6) return result, nil } @@ -254,7 +254,7 @@ func (b *menuBiz) RefreshMenus(ctx context.Context) error { return err } - key := keys.GetManageKey(ctx, keys.AllMenus) + key := know.GetManageKey(ctx, know.AllMenus) err = redis.Set(ctx, key, bs, time.Hour*6) return err } diff --git a/internal/erpserver/biz/v1/system/role.go b/internal/erpserver/biz/v1/system/role.go index 248a0ac..5c673d2 100644 --- a/internal/erpserver/biz/v1/system/role.go +++ b/internal/erpserver/biz/v1/system/role.go @@ -9,7 +9,7 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" "management/internal/erpserver/model/view" - "management/internal/global/keys" + "management/internal/pkg/know" "management/internal/pkg/redis" ) @@ -53,7 +53,7 @@ func (b *roleBiz) Update(ctx context.Context, arg *db.UpdateSysRoleParams) (*db. } func (b *roleBiz) All(ctx context.Context) ([]*db.SysRole, error) { - key := keys.GetManageKey(ctx, keys.AllRoles) + key := know.GetManageKey(ctx, know.AllRoles) bs, err := redis.GetBytes(ctx, key) if err == nil { var res []*db.SysRole @@ -115,7 +115,7 @@ func (b *roleBiz) Refresh(ctx context.Context) ([]*db.SysRole, error) { return nil, err } - key := keys.GetManageKey(ctx, keys.AllRoles) + key := know.GetManageKey(ctx, know.AllRoles) err = redis.Set(ctx, key, bs, time.Hour*6) if err != nil { return nil, err diff --git a/internal/erpserver/biz/v1/system/user.go b/internal/erpserver/biz/v1/system/user.go index 29bf9d1..466c3d9 100644 --- a/internal/erpserver/biz/v1/system/user.go +++ b/internal/erpserver/biz/v1/system/user.go @@ -11,8 +11,8 @@ import ( db "management/internal/db/sqlc" "management/internal/erpserver/model/req" "management/internal/erpserver/model/view" - "management/internal/global/know" "management/internal/pkg/crypto" + "management/internal/pkg/know" "management/internal/pkg/session" ) diff --git a/internal/erpserver/handler/budget/budget.go b/internal/erpserver/handler/budget/budget.go new file mode 100644 index 0000000..8060245 --- /dev/null +++ b/internal/erpserver/handler/budget/budget.go @@ -0,0 +1,274 @@ +package budget + +import ( + "errors" + "net/http" + "strconv" + "strings" + "time" + + "management/internal/db/model/form" + db "management/internal/db/sqlc" + "management/internal/erpserver/biz" + "management/internal/pkg/convertor" + "management/internal/pkg/know" + "management/internal/pkg/middleware" + "management/internal/pkg/tpl" + "management/internal/pkg/tpl/html" + + "github.com/jackc/pgx/v5/pgtype" +) + +type BudgetHandler interface { + List(w http.ResponseWriter, r *http.Request) + Add(w http.ResponseWriter, r *http.Request) + Edit(w http.ResponseWriter, r *http.Request) + Save(w http.ResponseWriter, r *http.Request) + Data(w http.ResponseWriter, r *http.Request) +} + +type budgetHandler struct { + render tpl.Renderer + biz biz.IBiz + mi middleware.IMiddleware +} + +var _ BudgetHandler = (*budgetHandler)(nil) + +func NewBudgetHandler(render tpl.Renderer, biz biz.IBiz, mi middleware.IMiddleware) *budgetHandler { + return &budgetHandler{ + render: render, + biz: biz, + mi: mi, + } +} + +func (h *budgetHandler) List(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + ctx := r.Context() + pp, _ := h.biz.ProjectV1().ListHtml(ctx) + cc, _ := h.biz.SystemV1().CategoryBiz().ListHtmlByLetter(ctx, know.BudgetCategory) + h.render.HTML(w, r, "budget/list.tmpl", map[string]any{ + "Statuses": html.NewSelectControls(html.Statuses, "0"), + "Projects": html.NewSelectControls(pp, "0"), + "Categories": html.NewSelectControls(cc, "0"), + }) + case http.MethodPost: + ctx := r.Context() + project := convertor.ConvertInt[int64](r.PostFormValue("project"), 9999) + if project == 0 { + project = 9999 + } + + budgetType := convertor.ConvertInt[int32](r.PostFormValue("budgetType"), 9999) + if budgetType == 0 { + budgetType = 9999 + } + + category := convertor.ConvertInt[int32](r.PostFormValue("category"), 9999) + if category == 0 { + category = 9999 + } + + title := strings.TrimSpace(r.PostFormValue("title")) + var search string + if len(title) > 0 { + search = "%" + title + "%" + if strings.HasSuffix(title, ":") { + search = title[:len(title)-1] + "%" + } + } + arg := &db.ListBudgetConditionParam{ + ProjectID: project, + BudgetType: budgetType, + Category: category, + IsTitle: len(search) > 0, + Title: search, + Status: convertor.ConvertInt[int16](r.PostFormValue("status"), 9999), + PageID: convertor.ConvertInt[int32](r.PostFormValue("page"), 1), + PageSize: convertor.ConvertInt[int32](r.PostFormValue("rows"), 10), + } + arg.TimeBegin, arg.TimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) + res, total, err := h.biz.BudgetV1().List(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + data := tpl.ResponseList{ + Code: 0, + Message: "ok", + Count: total, + Data: res, + } + h.render.JSON(w, data) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (h *budgetHandler) Add(w http.ResponseWriter, r *http.Request) { + h.render.HTML(w, r, "budget/edit.tmpl", map[string]any{ + "Item": &form.BudgetForm{ + BudgetType: 1, + }, + "Statuses": html.NewSelectControls(html.Statuses, "0"), + }) +} + +func (h *budgetHandler) Edit(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + id := convertor.QueryInt[int64](vars, "id", 0) + budget := &form.BudgetForm{} + ctx := r.Context() + if id > 0 { + if cus, err := h.biz.BudgetV1().Get(ctx, id); err == nil { + budget = budget.ToForm(cus) + if u, err := h.biz.SystemV1().UserBiz().Get(ctx, cus.CreatedUserID); err == nil { + budget.CreatedName = u.Username + } + if u, err := h.biz.SystemV1().UserBiz().Get(ctx, cus.UpdatedUserID); err == nil { + budget.UpdatedName = u.Username + } + } + } + h.render.HTML(w, r, "budget/edit.tmpl", map[string]any{ + "Item": budget, + "Statuses": html.NewSelectControls(html.Statuses, strconv.Itoa(int(budget.Status))), + }) +} + +func (h *budgetHandler) Save(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + data, err := validForm(r) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + authUser := h.mi.AuthUser(ctx) + if data.ID > 0 { + arg := &db.UpdateBudgetParams{ + ID: data.ID, + ProjectID: pgtype.Int8{ + Int64: data.ProjectID, + Valid: true, + }, + Name: pgtype.Text{ + String: data.Name, + Valid: true, + }, + BudgetType: pgtype.Int4{ + Int32: data.BudgetType, + Valid: true, + }, + Category: pgtype.Int4{ + Int32: data.Category, + Valid: true, + }, + StartAt: pgtype.Timestamptz{ + Time: data.StartAt, + Valid: true, + }, + EndAt: pgtype.Timestamptz{ + Time: data.EndAt, + Valid: true, + }, + Amount: data.AmountF, + UsedAmount: data.UsedAmountF, + RemainingAmount: data.RemainingAmountF, + Remark: pgtype.Text{ + String: data.Remark, + Valid: true, + }, + Status: pgtype.Int2{ + Int16: data.Status, + Valid: true, + }, + UpdatedUserID: pgtype.Int4{ + Int32: authUser.ID, + Valid: true, + }, + } + _, err := h.biz.BudgetV1().Update(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "更新成功") + } else { + arg := &db.CreateBudgetParams{ + ProjectID: data.ProjectID, + Name: data.Name, + BudgetType: data.BudgetType, + Category: data.Category, + StartAt: data.StartAt, + EndAt: data.EndAt, + Amount: data.AmountF, + UsedAmount: data.UsedAmountF, + RemainingAmount: data.RemainingAmountF, + Remark: data.Remark, + CreatedUserID: authUser.ID, + } + _, err := h.biz.BudgetV1().Create(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "添加成功") + } +} + +func (h *budgetHandler) Data(w http.ResponseWriter, r *http.Request) {} + +func validForm(r *http.Request) (form.BudgetForm, error) { + var err error + data := form.BudgetForm{} + + data.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) + data.ProjectID, err = strconv.ParseInt(r.PostFormValue("ProjectID"), 10, 64) + if err != nil || data.ProjectID == 0 { + return data, errors.New("项目不能为空") + } + + data.Name = r.PostFormValue("Name") + if len(data.Name) == 0 { + return data, errors.New("预算名称不能为空") + } + + budgetType, err := strconv.ParseInt(r.PostFormValue("BudgetType"), 10, 32) + if err != nil { + return data, errors.New("预算类型不能为空") + } + data.BudgetType = int32(budgetType) + + category, err := strconv.ParseInt(r.PostFormValue("Category"), 10, 32) + if err != nil { + return data, errors.New("预算类别不能为空") + } + data.Category = int32(category) + + data.StartAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("StartAt"), time.Local) + if err != nil { + return data, errors.New("开始时间格式错误") + } + data.EndAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("EndAt"), time.Local) + if err != nil { + return data, errors.New("结束时间格式错误") + } + + if err := data.AmountF.Scan(r.PostFormValue("Amount")); err != nil { + return data, errors.New("预算金额格式错误") + } + if err := data.UsedAmountF.Scan(r.PostFormValue("UsedAmount")); err != nil { + return data, errors.New("已支付金额格式错误") + } + if err := data.RemainingAmountF.Scan(r.PostFormValue("RemainingAmount")); err != nil { + return data, errors.New("剩余金额格式错误") + } + + data.Remark = r.PostFormValue("Remark") + data.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) + return data, nil +} diff --git a/internal/erpserver/handler/customer/customer.go b/internal/erpserver/handler/customer/customer.go index 43fa65b..35455e9 100644 --- a/internal/erpserver/handler/customer/customer.go +++ b/internal/erpserver/handler/customer/customer.go @@ -8,12 +8,12 @@ import ( "management/internal/db/model/form" db "management/internal/db/sqlc" "management/internal/erpserver/biz" - "management/internal/middleware/manage/auth" "management/internal/pkg/convertor" "management/internal/pkg/know" + "management/internal/pkg/middleware" "management/internal/pkg/snowflake" + "management/internal/pkg/tpl" "management/internal/pkg/tpl/html" - "management/internal/tpl" "github.com/jackc/pgx/v5/pgtype" ) @@ -28,14 +28,16 @@ type CustomerHandler interface { type customerHandler struct { render tpl.Renderer biz biz.IBiz + mi middleware.IMiddleware } var _ CustomerHandler = (*customerHandler)(nil) -func NewCustomerHandler(render tpl.Renderer, biz biz.IBiz) *customerHandler { +func NewCustomerHandler(render tpl.Renderer, biz biz.IBiz, mi middleware.IMiddleware) *customerHandler { return &customerHandler{ render: render, biz: biz, + mi: mi, } } @@ -143,7 +145,7 @@ func (h *customerHandler) Save(w http.ResponseWriter, r *http.Request) { } ctx := r.Context() - authUser := auth.AuthUser(ctx) + authUser := h.mi.AuthUser(ctx) if data.ID > 0 { arg := &db.UpdateCustomerParams{ ID: data.ID, diff --git a/internal/erpserver/handler/expense/expense.go b/internal/erpserver/handler/expense/expense.go new file mode 100644 index 0000000..b14a046 --- /dev/null +++ b/internal/erpserver/handler/expense/expense.go @@ -0,0 +1,231 @@ +package expense + +import ( + "errors" + "net/http" + "strconv" + "strings" + "time" + + "management/internal/db/model/form" + db "management/internal/db/sqlc" + "management/internal/erpserver/biz" + "management/internal/pkg/convertor" + "management/internal/pkg/know" + "management/internal/pkg/middleware" + "management/internal/pkg/tpl" + "management/internal/pkg/tpl/html" + + "github.com/jackc/pgx/v5/pgtype" +) + +type ExpenseHandler interface { + List(w http.ResponseWriter, r *http.Request) + Add(w http.ResponseWriter, r *http.Request) + Edit(w http.ResponseWriter, r *http.Request) + Save(w http.ResponseWriter, r *http.Request) +} + +type expenseHandler struct { + render tpl.Renderer + biz biz.IBiz + mi middleware.IMiddleware +} + +var _ ExpenseHandler = (*expenseHandler)(nil) + +func NewExpenseHandler(render tpl.Renderer, biz biz.IBiz, mi middleware.IMiddleware) *expenseHandler { + return &expenseHandler{ + render: render, + biz: biz, + mi: mi, + } +} + +func (h *expenseHandler) List(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + ctx := r.Context() + pp, _ := h.biz.ProjectV1().ListHtml(ctx) + cc, _ := h.biz.SystemV1().CategoryBiz().ListHtmlByLetter(ctx, know.ExpenseCategory) + h.render.HTML(w, r, "expense/list.tmpl", map[string]any{ + "Statuses": html.NewSelectControls(html.Statuses, "0"), + "Projects": html.NewSelectControls(pp, "0"), + "Categories": html.NewSelectControls(cc, "0"), + }) + case http.MethodPost: + ctx := r.Context() + project := convertor.ConvertInt[int64](r.PostFormValue("project"), 9999) + if project == 0 { + project = 9999 + } + budget := convertor.ConvertInt[int64](r.PostFormValue("budget"), 9999) + if budget == 0 { + budget = 9999 + } + category := convertor.ConvertInt[int32](r.PostFormValue("category"), 9999) + if category == 0 { + category = 9999 + } + + title := strings.TrimSpace(r.PostFormValue("title")) + var search string + if len(title) > 0 { + search = "%" + title + "%" + if strings.HasSuffix(title, ":") { + search = title[:len(title)-1] + "%" + } + } + arg := &db.ListExpenseConditionParam{ + ProjectID: project, + BudgetID: budget, + ExpenseType: category, + IsTitle: len(search) > 0, + Title: search, + Status: convertor.ConvertInt[int16](r.PostFormValue("status"), 9999), + PageID: convertor.ConvertInt[int32](r.PostFormValue("page"), 1), + PageSize: convertor.ConvertInt[int32](r.PostFormValue("rows"), 10), + } + arg.TimeBegin, arg.TimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) + res, total, err := h.biz.ExpenseV1().List(ctx, arg) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + data := tpl.ResponseList{ + Code: 0, + Message: "ok", + Count: total, + Data: res, + } + h.render.JSON(w, data) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (h *expenseHandler) Add(w http.ResponseWriter, r *http.Request) { + h.render.HTML(w, r, "expense/edit.tmpl", map[string]any{ + "Item": &form.ExpenseForm{}, + "Statuses": html.NewSelectControls(html.Statuses, "0"), + }) +} + +func (h *expenseHandler) Edit(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + id := convertor.QueryInt[int64](vars, "id", 0) + expense := &form.ExpenseForm{} + ctx := r.Context() + if id > 0 { + if cus, err := h.biz.ExpenseV1().Get(ctx, id); err == nil { + expense = expense.ToForm(cus) + if u, err := h.biz.SystemV1().UserBiz().Get(ctx, cus.CreatedUserID); err == nil { + expense.CreatedName = u.Username + } + if u, err := h.biz.SystemV1().UserBiz().Get(ctx, cus.UpdatedUserID); err == nil { + expense.UpdatedName = u.Username + } + } + } + h.render.HTML(w, r, "expense/edit.tmpl", map[string]any{ + "Item": expense, + "Statuses": html.NewSelectControls(html.Statuses, strconv.Itoa(int(expense.Status))), + }) +} + +func (h *expenseHandler) Save(w http.ResponseWriter, r *http.Request) { + data, err := validForm(r) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + ctx := r.Context() + authUser := h.mi.AuthUser(ctx) + if data.ID > 0 { + arg := &db.UpdateExpenseParams{ + ID: data.ID, + ProjectID: pgtype.Int8{ + Int64: data.ProjectID, + Valid: true, + }, + BudgetID: pgtype.Int8{ + Int64: data.BudgetID, + Valid: true, + }, + Amount: data.AmountF, + ExpensesAt: pgtype.Timestamptz{ + Time: data.ExpensesAt, + Valid: true, + }, + ExpensesType: pgtype.Int4{ + Int32: data.ExpensesType, + Valid: true, + }, + Remark: pgtype.Text{ + String: data.Remark, + Valid: true, + }, + Status: pgtype.Int2{ + Int16: data.Status, + Valid: true, + }, + UpdatedUserID: pgtype.Int4{ + Int32: authUser.ID, + Valid: true, + }, + } + _, err := h.biz.ExpenseV1().Update(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "更新成功") + } else { + arg := &db.CreateExpenseParams{ + ProjectID: data.ProjectID, + BudgetID: data.BudgetID, + Amount: data.AmountF, + ExpensesAt: data.ExpensesAt, + ExpensesType: data.ExpensesType, + Remark: data.Remark, + Status: data.Status, + CreatedUserID: authUser.ID, + } + _, err := h.biz.ExpenseV1().Create(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "添加成功") + } +} + +func validForm(r *http.Request) (form.ExpenseForm, error) { + var err error + data := form.ExpenseForm{} + + data.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) + data.ProjectID, err = strconv.ParseInt(r.PostFormValue("ProjectID"), 10, 64) + if err != nil || data.ProjectID == 0 { + return data, errors.New("项目不能为空") + } + data.BudgetID, err = strconv.ParseInt(r.PostFormValue("BudgetID"), 10, 64) + + if err := data.AmountF.Scan(r.PostFormValue("Amount")); err != nil { + return data, errors.New("报销金额格式错误") + } + data.ExpensesAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("ExpensesAt"), time.Local) + if err != nil { + return data, errors.New("报销时间格式错误") + } + expensesType, err := strconv.ParseInt(r.PostFormValue("ExpensesType"), 10, 64) + if err != nil || expensesType == 0 { + return data, errors.New("报销类型不能为空") + } + data.ExpensesType = int32(expensesType) + + data.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) + return data, nil +} diff --git a/internal/erpserver/handler/handler.go b/internal/erpserver/handler/handler.go index aa731dc..873cf55 100644 --- a/internal/erpserver/handler/handler.go +++ b/internal/erpserver/handler/handler.go @@ -3,10 +3,14 @@ package handler import ( "management/internal/config" "management/internal/erpserver/biz" + "management/internal/erpserver/handler/budget" "management/internal/erpserver/handler/common" "management/internal/erpserver/handler/customer" + "management/internal/erpserver/handler/expense" + "management/internal/erpserver/handler/income" "management/internal/erpserver/handler/project" "management/internal/erpserver/handler/system" + "management/internal/pkg/middleware" "management/internal/pkg/redis" "management/internal/pkg/session" "management/internal/pkg/tpl" @@ -18,8 +22,11 @@ type IHandler interface { CommonHandler() common.CommonHandler // 获取 System Handler 接口. SystemHandler() system.SystemHandler - PorjectHandler() project.PorjectHandler + ProjectHandler() project.ProjectHandler + BudgetHandler() budget.BudgetHandler CustomerHandler() customer.CustomerHandler + IncomeHandler() income.IncomeHandler + ExpenseHandler() expense.ExpenseHandler } // handler 是 IHandler 的一个具体实现. @@ -29,19 +36,21 @@ type handler struct { redis redis.IRedis session session.ISession biz biz.IBiz + mi middleware.IMiddleware } // 确保 handler 实现了 IHandler 接口. var _ IHandler = (*handler)(nil) // NewHandler 创建一个 IHandler 类型的实例. -func NewHandler(conf *config.Config, render tpl.Renderer, redis redis.IRedis, session session.ISession, biz biz.IBiz) *handler { +func NewHandler(conf *config.Config, render tpl.Renderer, redis redis.IRedis, session session.ISession, biz biz.IBiz, mi middleware.IMiddleware) *handler { return &handler{ conf: conf, render: render, redis: redis, session: session, biz: biz, + mi: mi, } } @@ -52,15 +61,30 @@ func (h *handler) CommonHandler() common.CommonHandler { // SystemHandler 返回一个实现了 SystemHandler 接口的实例. func (h *handler) SystemHandler() system.SystemHandler { - return system.NewSystemHandler(h.render, h.redis, h.session, h.biz) + return system.NewSystemHandler(h.render, h.redis, h.session, h.biz, h.mi) } -// PorjectHandler 返回一个实现了 PorjectHandler 接口的实例. -func (h *handler) PorjectHandler() project.PorjectHandler { - return project.NewPorjectHandler(h.render, h.biz) +// ProjectHandler 返回一个实现了 ProjectHandler 接口的实例. +func (h *handler) ProjectHandler() project.ProjectHandler { + return project.NewProjectHandler(h.render, h.biz, h.mi) +} + +// BudgetHandler 返回一个实现了 BudgetHandler 接口的实例. +func (h *handler) BudgetHandler() budget.BudgetHandler { + return budget.NewBudgetHandler(h.render, h.biz, h.mi) } // CustomerHandler 返回一个实现了 CustomerHandler 接口的实例. func (h *handler) CustomerHandler() customer.CustomerHandler { - return customer.NewCustomerHandler(h.render, h.biz) + return customer.NewCustomerHandler(h.render, h.biz, h.mi) +} + +// IncomeHandler 返回一个实现了 IncomeHandler 接口的实例. +func (h *handler) IncomeHandler() income.IncomeHandler { + return income.NewIncomeHandler(h.render, h.biz, h.mi) +} + +// ExpenseHandler 返回一个实现了 ExpenseHandler 接口的实例. +func (h *handler) ExpenseHandler() expense.ExpenseHandler { + return expense.NewExpenseHandler(h.render, h.biz, h.mi) } diff --git a/internal/erpserver/handler/income/income.go b/internal/erpserver/handler/income/income.go new file mode 100644 index 0000000..4723851 --- /dev/null +++ b/internal/erpserver/handler/income/income.go @@ -0,0 +1,249 @@ +package income + +import ( + "errors" + "net/http" + "strconv" + "strings" + "time" + + "management/internal/db/model/form" + db "management/internal/db/sqlc" + "management/internal/erpserver/biz" + "management/internal/pkg/convertor" + "management/internal/pkg/know" + "management/internal/pkg/middleware" + "management/internal/pkg/tpl" + "management/internal/pkg/tpl/html" + + "github.com/jackc/pgx/v5/pgtype" +) + +type IncomeHandler interface { + List(w http.ResponseWriter, r *http.Request) + Add(w http.ResponseWriter, r *http.Request) + Edit(w http.ResponseWriter, r *http.Request) + Save(w http.ResponseWriter, r *http.Request) +} + +type incomeHandler struct { + render tpl.Renderer + biz biz.IBiz + mi middleware.IMiddleware +} + +var _ IncomeHandler = (*incomeHandler)(nil) + +func NewIncomeHandler(render tpl.Renderer, biz biz.IBiz, mi middleware.IMiddleware) *incomeHandler { + return &incomeHandler{ + render: render, + biz: biz, + mi: mi, + } +} + +func (h *incomeHandler) List(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + ctx := r.Context() + pp, _ := h.biz.ProjectV1().ListHtml(ctx) + cc, _ := h.biz.SystemV1().CategoryBiz().ListHtmlByLetter(ctx, know.IncomeCategory) + h.render.HTML(w, r, "income/list.tmpl", map[string]any{ + "Statuses": html.NewSelectControls(html.Statuses, "0"), + "Projects": html.NewSelectControls(pp, "0"), + "Categories": html.NewSelectControls(cc, "0"), + }) + case http.MethodPost: + ctx := r.Context() + project := convertor.ConvertInt[int64](r.PostFormValue("project"), 9999) + if project == 0 { + project = 9999 + } + budget := convertor.ConvertInt[int64](r.PostFormValue("budget"), 9999) + if budget == 0 { + budget = 9999 + } + category := convertor.ConvertInt[int32](r.PostFormValue("category"), 9999) + if category == 0 { + category = 9999 + } + + title := strings.TrimSpace(r.PostFormValue("title")) + var search string + if len(title) > 0 { + search = "%" + title + "%" + if strings.HasSuffix(title, ":") { + search = title[:len(title)-1] + "%" + } + } + arg := &db.ListIncomeConditionParam{ + ProjectID: project, + BudgetID: budget, + IncomeType: category, + IsTitle: len(search) > 0, + Title: search, + Status: convertor.ConvertInt[int16](r.PostFormValue("status"), 9999), + PageID: convertor.ConvertInt[int32](r.PostFormValue("page"), 1), + PageSize: convertor.ConvertInt[int32](r.PostFormValue("rows"), 10), + } + arg.TimeBegin, arg.TimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) + res, total, err := h.biz.IncomeV1().List(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + data := tpl.ResponseList{ + Code: 0, + Message: "ok", + Count: total, + Data: res, + } + h.render.JSON(w, data) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (h *incomeHandler) Add(w http.ResponseWriter, r *http.Request) { + h.render.HTML(w, r, "income/edit.tmpl", map[string]any{ + "Item": &form.IncomeForm{}, + "Statuses": html.NewSelectControls(html.Statuses, "0"), + }) +} + +func (h *incomeHandler) Edit(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + id := convertor.ConvertInt[int64](vars.Get("id"), 0) + income := &form.IncomeForm{} + ctx := r.Context() + if id > 0 { + if cus, err := h.biz.IncomeV1().Get(ctx, id); err == nil { + income = income.ToForm(cus) + if u, err := h.biz.SystemV1().UserBiz().Get(ctx, cus.CreatedUserID); err == nil { + income.CreatedName = u.Username + } + if u, err := h.biz.SystemV1().UserBiz().Get(ctx, cus.UpdatedUserID); err == nil { + income.UpdatedName = u.Username + } + } + } + h.render.HTML(w, r, "income/edit.tmpl", map[string]any{ + "Item": income, + "Statuses": html.NewSelectControls(html.Statuses, strconv.Itoa(int(income.Status))), + }) +} + +func (h *incomeHandler) Save(w http.ResponseWriter, r *http.Request) { + data, err := validForm(r) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + ctx := r.Context() + authUser := h.mi.AuthUser(ctx) + if data.ID > 0 { + arg := &db.UpdateIncomeParams{ + ID: data.ID, + ProjectID: pgtype.Int8{ + Int64: data.ProjectID, + Valid: true, + }, + BudgetID: pgtype.Int8{ + Int64: data.BudgetID, + Valid: true, + }, + Amount: data.AmountF, + IncomeAt: pgtype.Timestamptz{ + Time: data.IncomeAt, + Valid: true, + }, + IncomeType: pgtype.Int4{ + Int32: data.IncomeType, + Valid: true, + }, + IncomeBank: pgtype.Int4{ + Int32: data.IncomeBank, + Valid: true, + }, + Remark: pgtype.Text{ + String: data.Remark, + Valid: true, + }, + Status: pgtype.Int2{ + Int16: data.Status, + Valid: true, + }, + UpdatedUserID: pgtype.Int4{ + Int32: authUser.ID, + Valid: true, + }, + } + _, err := h.biz.IncomeV1().Update(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "更新成功") + } else { + arg := &db.CreateIncomeParams{ + ProjectID: data.ProjectID, + BudgetID: data.BudgetID, + Amount: data.AmountF, + IncomeAt: data.IncomeAt, + IncomeType: data.IncomeType, + IncomeBank: data.IncomeBank, + Remark: data.Remark, + Status: data.Status, + CreatedUserID: authUser.ID, + } + _, err := h.biz.IncomeV1().Create(ctx, arg) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSONOK(w, "添加成功") + } +} + +func validForm(r *http.Request) (form.IncomeForm, error) { + var err error + data := form.IncomeForm{} + data.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) + + data.ProjectID, err = strconv.ParseInt(r.PostFormValue("ProjectID"), 10, 64) + if err != nil || data.ProjectID == 0 { + return data, errors.New("项目不能为空") + } + + data.BudgetID, err = strconv.ParseInt(r.PostFormValue("BudgetID"), 10, 64) + if err != nil { + data.BudgetID = 0 + } + + if err := data.AmountF.Scan(r.PostFormValue("Amount")); err != nil { + return data, errors.New("报销金额格式错误") + } + + data.IncomeAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("IncomeAt"), time.Local) + if err != nil { + return data, errors.New("回款时间格式错误") + } + + incomeType, err := strconv.ParseInt(r.PostFormValue("IncomeType"), 10, 64) + if err != nil { + return data, errors.New("收入类型数据错误") + } + data.IncomeType = int32(incomeType) + + incomeBank, err := strconv.ParseInt(r.PostFormValue("IncomeBank"), 10, 64) + if err != nil { + return data, errors.New("收入银行数据错误") + } + data.IncomeBank = int32(incomeBank) + + data.Remark = r.PostFormValue("Remark") + data.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) + return data, nil +} diff --git a/internal/erpserver/handler/project/dashboard.go b/internal/erpserver/handler/project/dashboard.go new file mode 100644 index 0000000..ae4bd12 --- /dev/null +++ b/internal/erpserver/handler/project/dashboard.go @@ -0,0 +1,369 @@ +package project + +import ( + "context" + "fmt" + "net/http" + + "management/internal/db/model/view" + "management/internal/pkg/convertor" + "management/internal/pkg/tpl" +) + +func (h *projectHandler) Dashboard(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + pp, _ := h.biz.ProjectV1().ListHtml(r.Context()) + h.render.HTML(w, r, "project/dashboard.tmpl", map[string]any{ + "Projects": pp, + }) + case http.MethodPost: + ctx := r.Context() + t := r.PostFormValue("type") + if t == "project" { + var projectIncome float64 + var projectExpense float64 + projectID := convertor.ConvertInt[int64](r.PostFormValue("projectID"), 0) + if projectID == 0 { + si, _ := h.biz.IncomeV1().Sum(ctx) + projectIncome = convertor.NumericToFloat64(si) + se, _ := h.biz.ExpenseV1().Sum(ctx) + projectExpense = convertor.NumericToFloat64(se) + } else { + pi, _ := h.biz.IncomeV1().SumByProjectID(ctx, projectID) + projectIncome = convertor.NumericToFloat64(pi) + pe, _ := h.biz.ExpenseV1().SumByProjectID(ctx, projectID) + projectExpense = convertor.NumericToFloat64(pe) + } + + projectProfit := projectIncome - projectExpense + var projectProfitRate float64 = 0 + if projectExpense > 0 { + projectProfitRate = projectProfit / projectExpense + } + + res := &view.DashboardProject{ + ProjectIncome: projectIncome, + ProjectExpense: projectExpense, + ProjectProfit: projectProfit, + ProjectProfitRate: fmt.Sprintf("%0.2f%%", projectProfitRate*100), + IncomeExpenseEcharts: h.incomeExpenseEcharts(ctx, projectID), + IncomeEcharts: h.incomeEcharts(ctx, projectID), + ExpenseEcharts: h.expenseEcharts(ctx, projectID), + } + h.render.JSON(w, tpl.Response{Success: true, Message: "ok", Data: res}) + return + } else if t == "budget" { + } + h.render.JSONERR(w, "failed to valid type") + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func (h *projectHandler) incomeExpenseEcharts(ctx context.Context, projectId int64) view.EchartsOption { + var name []string + var income []float64 + var expense []float64 + + if projectId == 0 { + rows, err := h.biz.ProjectV1().Statistics(ctx) + if err != nil || len(rows) == 0 { + return view.EchartsOption{} + } + + for _, row := range rows { + name = append(name, row.Name) + income = append(income, convertor.NumericToFloat64(row.Income)) + expense = append(expense, convertor.NumericToFloat64(row.Expense)) + } + } else { + row, err := h.biz.ProjectV1().StatisticsItem(ctx, projectId) + if err != nil || row == nil { + return view.EchartsOption{} + } + name = append(name, row.Name) + income = append(income, convertor.NumericToFloat64(row.Income)) + expense = append(expense, convertor.NumericToFloat64(row.Expense)) + } + + return view.EchartsOption{ + Title: view.Title{ + Text: "项目收支情况", + Left: "center", + Top: 2, + FontSize: 15, + TextStyle: view.TextStyle{ + Color: "#666666", + FontWeight: "normal", + }, + }, + Color: []string{"#fed46b", "#2194ff"}, + ToolTip: view.ToolTip{ + Trigger: "axis", + AxisPointer: view.AxisPointer{ + Type: "shadow", + }, + }, + Grid: view.Grid{ + Left: "3%", + Right: "4%", + Bottom: "10%", + ContainLabel: true, + }, + Legend: view.Legend{ + Left: "center", + Top: "bottom", + Data: []string{"支出金额", "收入金额"}, + }, + XAxis: []view.XAxis{ + { + Type: "category", + Data: name, + AxisTick: view.AxisTick{ + AlignWithLabel: true, + }, + }, + }, + YAxis: []view.YAxis{ + { + Type: "value", + Name: "单位:元", + }, + }, + BarMaxWidth: "30", + Label: view.Label{ + Show: true, + Position: "top", + }, + Series: []view.Series{ + { + Name: "支出金额", + Type: "bar", + Data: expense, + ItemStyle: view.ItemStyle{ + Normal: view.Normal{ + Color: "#f89588", + }, + }, + }, + { + Name: "收入金额", + Type: "bar", + Data: income, + ItemStyle: view.ItemStyle{ + Normal: view.Normal{ + Color: "#76da91", + }, + }, + }, + }, + } +} + +func (h *projectHandler) incomeEcharts(ctx context.Context, projectId int64) view.EchartsOption { + var name []string + data := []view.DataItem{} + + if projectId == 0 { + rows, err := h.biz.IncomeV1().Statistics(ctx) + if err != nil || len(rows) == 0 { + return view.EchartsOption{} + } + + for _, row := range rows { + name = append(name, row.IncomeTypeName) + data = append(data, view.DataItem{ + Name: row.IncomeTypeName, + Value: convertor.NumericToFloat64(row.TotalAmount), + }) + } + } else { + rows, err := h.biz.IncomeV1().StatisticsByProjectID(ctx, projectId) + if err != nil || len(rows) == 0 { + return view.EchartsOption{} + } + + for _, row := range rows { + name = append(name, row.IncomeTypeName) + data = append(data, view.DataItem{ + Name: row.IncomeTypeName, + Value: convertor.NumericToFloat64(row.TotalAmount), + }) + } + } + + return view.EchartsOption{ + Title: view.Title{ + Text: "收入分布", + Left: "center", + Orient: "vertical", + FontSize: 15, + TextStyle: view.TextStyle{ + Color: "#666666", + FontWeight: "normal", + }, + }, + ToolTip: view.ToolTip{ + Trigger: "item", + }, + Legend: view.Legend{ + Left: "center", + Top: "bottom", + Data: name, + }, + Color: []string{"#63b2ee", "#76da91", "#f8cb7f", "#f89588", "#7cd6cf", "#9192ab", "#7898e1", "#efa666", "#eddd86", "#9987ce", "#63b2ee", "#76da91"}, + Series: []view.Series{ + { + Type: "pie", + Radius: "50%", + Data: data, + Label: view.Label{ + Show: true, + TextStyle: view.TextStyle{ + Color: "#666666", + }, + Normal: view.LableNormal{ + Formatter: "{c} ({d}%)", + TextStyle: view.TextStyle{ + Color: "#666666", + FontWeight: "normal", + }, + }, + }, + }, + }, + } +} + +func (h *projectHandler) expenseEcharts(ctx context.Context, projectId int64) view.EchartsOption { + var name []string + data := []view.DataItem{} + + if projectId == 0 { + rows, err := h.biz.ExpenseV1().Statistics(ctx) + if err != nil || len(rows) == 0 { + return view.EchartsOption{} + } + + for _, row := range rows { + name = append(name, row.ExpensesTypeName) + data = append(data, view.DataItem{ + Name: row.ExpensesTypeName, + Value: convertor.NumericToFloat64(row.TotalAmount), + }) + } + } else { + rows, err := h.biz.ExpenseV1().StatisticsByProjectID(ctx, projectId) + if err != nil || len(rows) == 0 { + return view.EchartsOption{} + } + + for _, row := range rows { + name = append(name, row.ExpensesTypeName) + data = append(data, view.DataItem{ + Name: row.ExpensesTypeName, + Value: convertor.NumericToFloat64(row.TotalAmount), + }) + } + } + + return view.EchartsOption{ + Title: view.Title{ + Text: "支出分布", + Left: "center", + Orient: "vertical", + FontSize: 15, + TextStyle: view.TextStyle{ + Color: "#666666", + FontWeight: "normal", + }, + }, + ToolTip: view.ToolTip{ + Trigger: "item", + }, + Legend: view.Legend{ + Left: "center", + Top: "bottom", + Data: name, + }, + Color: []string{"#63b2ee", "#76da91", "#f8cb7f", "#f89588", "#7cd6cf", "#9192ab", "#7898e1", "#efa666", "#eddd86", "#9987ce", "#63b2ee", "#76da91"}, + Series: []view.Series{ + { + Type: "pie", + Radius: "50%", + Data: data, + Label: view.Label{ + Show: true, + TextStyle: view.TextStyle{ + Color: "#666666", + }, + Normal: view.LableNormal{ + Formatter: "{c} ({d}%)", + TextStyle: view.TextStyle{ + Color: "#666666", + FontWeight: "normal", + }, + }, + }, + }, + }, + } + + // return view.EchartsOption{ + // Title: view.Title{ + // Text: "支出分布", + // Left: "center", + // Top: 2, + // FontSize: 15, + // TextStyle: view.TextStyle{ + // Color: "#666666", + // FontWeight: "normal", + // }, + // }, + // Color: []string{"#fed46b", "#2194ff"}, + // ToolTip: view.ToolTip{ + // Trigger: "axis", + // AxisPointer: view.AxisPointer{ + // Type: "shadow", + // }, + // }, + // Grid: view.Grid{ + // Left: "3%", + // Right: "4%", + // Bottom: "10%", + // ContainLabel: true, + // }, + // XAxis: []view.XAxis{ + // { + // Type: "category", + // Data: name, + // AxisTick: view.AxisTick{ + // AlignWithLabel: true, + // }, + // }, + // }, + // YAxis: []view.YAxis{ + // { + // Type: "value", + // Name: "单位:元", + // }, + // }, + // BarMaxWidth: "30", + // Label: view.Label{ + // Show: true, + // Position: "top", + // }, + // Series: []view.Series{ + // { + // Type: "bar", + // Data: expense, + // ItemStyle: view.ItemStyle{ + // Normal: view.Normal{ + // Color: "#f89588", + // }, + // }, + // }, + // }, + // } +} diff --git a/internal/erpserver/handler/project/project.go b/internal/erpserver/handler/project/project.go index 18cbbc9..2ab8ee8 100644 --- a/internal/erpserver/handler/project/project.go +++ b/internal/erpserver/handler/project/project.go @@ -10,43 +10,45 @@ import ( formDto "management/internal/db/model/form" db "management/internal/db/sqlc" "management/internal/erpserver/biz" - "management/internal/global" - "management/internal/global/html" - "management/internal/middleware/manage/auth" "management/internal/pkg/convertor" + "management/internal/pkg/middleware" "management/internal/pkg/snowflake" - "management/internal/router/manage/util" - "management/internal/tpl" + "management/internal/pkg/tpl" + "management/internal/pkg/tpl/html" "github.com/jackc/pgx/v5/pgtype" ) -type PorjectHandler interface { +type ProjectHandler interface { List(w http.ResponseWriter, r *http.Request) Add(w http.ResponseWriter, r *http.Request) Edit(w http.ResponseWriter, r *http.Request) Save(w http.ResponseWriter, r *http.Request) + Data(w http.ResponseWriter, r *http.Request) + Dashboard(w http.ResponseWriter, r *http.Request) } -type porjectHandler struct { +type projectHandler struct { render tpl.Renderer biz biz.IBiz + mi middleware.IMiddleware } -var _ PorjectHandler = (*porjectHandler)(nil) +var _ ProjectHandler = (*projectHandler)(nil) -func NewPorjectHandler(render tpl.Renderer, biz biz.IBiz) *porjectHandler { - return &porjectHandler{ +func NewProjectHandler(render tpl.Renderer, biz biz.IBiz, mi middleware.IMiddleware) *projectHandler { + return &projectHandler{ render: render, biz: biz, + mi: mi, } } -func (h *porjectHandler) List(w http.ResponseWriter, r *http.Request) { +func (h *projectHandler) List(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: h.render.HTML(w, r, "project/list.tmpl", map[string]any{ - "Statuses": html.NewSelectControls(global.ProjectStatuses, 0), + "Statuses": html.NewSelectControls(html.ProjectStatuses, "0"), }) case http.MethodPost: title := strings.TrimSpace(r.PostFormValue("title")) @@ -64,7 +66,7 @@ func (h *porjectHandler) List(w http.ResponseWriter, r *http.Request) { PageID: convertor.ConvertInt[int32](r.PostFormValue("page"), 1), PageSize: convertor.ConvertInt[int32](r.PostFormValue("rows"), 10), } - arg.TimeBegin, arg.TimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) + arg.TimeBegin, arg.TimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) res, total, err := h.biz.ProjectV1().List(r.Context(), arg) if err != nil { h.render.JSONERR(w, err.Error()) @@ -83,8 +85,8 @@ func (h *porjectHandler) List(w http.ResponseWriter, r *http.Request) { } } -func (h *porjectHandler) Add(w http.ResponseWriter, r *http.Request) { - authUser := auth.AuthUser(r.Context()) +func (h *projectHandler) Add(w http.ResponseWriter, r *http.Request) { + authUser := h.mi.AuthUser(r.Context()) h.render.HTML(w, r, "project/edit.tmpl", map[string]any{ "Item": &formDto.ProjectForm{ ApplyUserID: authUser.ID, @@ -92,11 +94,11 @@ func (h *porjectHandler) Add(w http.ResponseWriter, r *http.Request) { ProjectFileItems: []*formDto.ProjectFileItemForm{}, }, }, - "Statuses": html.NewSelectControls(global.ProjectStatuses, 0), + "Statuses": html.NewSelectControls(html.ProjectStatuses, "0"), }) } -func (h *porjectHandler) Edit(w http.ResponseWriter, r *http.Request) { +func (h *projectHandler) Edit(w http.ResponseWriter, r *http.Request) { form := &formDto.ProjectForm{} id := convertor.ConvertInt[int64](r.URL.Query().Get("id"), 0) if id > 0 { @@ -109,7 +111,7 @@ func (h *porjectHandler) Edit(w http.ResponseWriter, r *http.Request) { } form = form.ToForm(po, pfs) if form.ApplyUserID == 0 { - authUser := auth.AuthUser(ctx) + authUser := h.mi.AuthUser(ctx) form.ApplyUserID = authUser.ID } // if c, err := db.Engine.GetCustomer(ctx, po.CustomerID); err == nil { @@ -125,19 +127,19 @@ func (h *porjectHandler) Edit(w http.ResponseWriter, r *http.Request) { } h.render.HTML(w, r, "project/edit.tmpl", map[string]any{ "Item": form, - "Statuses": html.NewSelectControls(global.ProjectStatuses, form.Status), + "Statuses": html.NewSelectControls(html.ProjectStatuses, strconv.Itoa(int(form.Status))), }) } -func (h *porjectHandler) Save(w http.ResponseWriter, r *http.Request) { +func (h *projectHandler) Save(w http.ResponseWriter, r *http.Request) { ctx := r.Context() form, err := validForm(r) if err != nil { - tpl.JSONERR(w, err.Error()) + h.render.JSONERR(w, err.Error()) return } - authUser := auth.AuthUser(ctx) + authUser := h.mi.AuthUser(ctx) if form.ID > 0 { p := &db.UpdateProjectParams{ ID: form.ID, @@ -199,10 +201,10 @@ func (h *porjectHandler) Save(w http.ResponseWriter, r *http.Request) { } err := h.biz.ProjectV1().Update(ctx, p, cpfs) if err != nil { - tpl.JSONERR(w, err.Error()) + h.render.JSONERR(w, err.Error()) return } - tpl.JSONOK(w, "更新成功") + h.render.JSONOK(w, "更新成功") } else { p := &db.CreateProjectParams{ ID: snowflake.GetId(), @@ -231,13 +233,30 @@ func (h *porjectHandler) Save(w http.ResponseWriter, r *http.Request) { } err := h.biz.ProjectV1().Create(ctx, p, cpfs) if err != nil { - tpl.JSONERR(w, err.Error()) + h.render.JSONERR(w, err.Error()) return } - tpl.JSONOK(w, "添加成功") + h.render.JSONOK(w, "添加成功") } } +func (h *projectHandler) Data(w http.ResponseWriter, r *http.Request) { + vars := r.URL.Query() + t := vars.Get("type") + if t == "xmselect" { + res, err := h.biz.ProjectV1().XmSelect(r.Context()) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + + h.render.JSON(w, res) + return + } + + h.render.JSON(w, nil) +} + func validForm(r *http.Request) (formDto.ProjectForm, error) { var err error form := formDto.ProjectForm{} diff --git a/internal/erpserver/handler/system/audit.go b/internal/erpserver/handler/system/audit.go index 095588e..d7196a5 100644 --- a/internal/erpserver/handler/system/audit.go +++ b/internal/erpserver/handler/system/audit.go @@ -7,7 +7,6 @@ import ( "management/internal/erpserver/biz" "management/internal/pkg/convertor" "management/internal/pkg/tpl" - "management/internal/router/manage/util" ) type AuditHandler interface { @@ -34,7 +33,7 @@ func (h *auditHandler) List(w http.ResponseWriter, r *http.Request) { h.render.HTML(w, r, "audit_log/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto - q.SearchTimeBegin, q.SearchTimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) + q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) q.SearchName = r.PostFormValue("name") q.SearchEmail = r.PostFormValue("email") q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) diff --git a/internal/erpserver/handler/system/category.go b/internal/erpserver/handler/system/category.go index 4839788..ab363c2 100644 --- a/internal/erpserver/handler/system/category.go +++ b/internal/erpserver/handler/system/category.go @@ -214,6 +214,15 @@ func (h *categoryHandler) Data(w http.ResponseWriter, r *http.Request) { return } + h.render.JSON(w, res) + return + } else if t == "xmselect" { + res, err := h.biz.SystemV1().CategoryBiz().XmSelect(ctx, vars.Get("letter")) + if err != nil { + h.render.JSONERR(w, err.Error()) + return + } + h.render.JSON(w, res) return } diff --git a/internal/erpserver/handler/system/config.go b/internal/erpserver/handler/system/config.go index 3575cf1..65c2e48 100644 --- a/internal/erpserver/handler/system/config.go +++ b/internal/erpserver/handler/system/config.go @@ -8,8 +8,8 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" "management/internal/erpserver/biz" - "management/internal/global/pearadmin" "management/internal/pkg/convertor" + "management/internal/pkg/know/pearadmin" "management/internal/pkg/redis" "management/internal/pkg/tpl" ) diff --git a/internal/erpserver/handler/system/login_log.go b/internal/erpserver/handler/system/login_log.go index 9e5e5fd..e592e28 100644 --- a/internal/erpserver/handler/system/login_log.go +++ b/internal/erpserver/handler/system/login_log.go @@ -7,7 +7,6 @@ import ( "management/internal/erpserver/biz" "management/internal/pkg/convertor" "management/internal/pkg/tpl" - "management/internal/router/manage/util" ) type LoginLogHandler interface { @@ -34,7 +33,7 @@ func (h *loginLogHandler) List(w http.ResponseWriter, r *http.Request) { h.render.HTML(w, r, "login_log/list.tmpl", nil) case http.MethodPost: var q dto.SearchDto - q.SearchTimeBegin, q.SearchTimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) + q.SearchTimeBegin, q.SearchTimeEnd = convertor.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) q.SearchName = r.PostFormValue("name") q.SearchEmail = r.PostFormValue("email") q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) diff --git a/internal/erpserver/handler/system/menu.go b/internal/erpserver/handler/system/menu.go index d267ad8..9622b49 100644 --- a/internal/erpserver/handler/system/menu.go +++ b/internal/erpserver/handler/system/menu.go @@ -10,8 +10,8 @@ import ( "management/internal/db/model/dto" db "management/internal/db/sqlc" "management/internal/erpserver/biz" - "management/internal/global/know" "management/internal/pkg/convertor" + "management/internal/pkg/know" "management/internal/pkg/session" "management/internal/pkg/tpl" diff --git a/internal/erpserver/handler/system/role.go b/internal/erpserver/handler/system/role.go index 53a8765..93da345 100644 --- a/internal/erpserver/handler/system/role.go +++ b/internal/erpserver/handler/system/role.go @@ -11,7 +11,6 @@ import ( "management/internal/erpserver/biz" "management/internal/pkg/convertor" "management/internal/pkg/tpl" - "management/internal/router/manage/util" ) type RoleHandler interface { @@ -302,7 +301,7 @@ func (h *roleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { var menuList []*db.SysMenu for _, v := range menuArr { - menuID := util.ConvertInt(v, 0) + menuID := convertor.ConvertInt(v, 0) if menuID > 0 { menu, err := h.biz.SystemV1().MenuBiz().Get(ctx, int32(menuID)) if err != nil { diff --git a/internal/erpserver/handler/system/system.go b/internal/erpserver/handler/system/system.go index bc45d33..9459de0 100644 --- a/internal/erpserver/handler/system/system.go +++ b/internal/erpserver/handler/system/system.go @@ -4,6 +4,7 @@ import ( "net/http" "management/internal/erpserver/biz" + "management/internal/pkg/middleware" "management/internal/pkg/redis" "management/internal/pkg/session" "management/internal/pkg/tpl" @@ -26,16 +27,18 @@ type systemHandler struct { redis redis.IRedis session session.ISession biz biz.IBiz + mi middleware.IMiddleware } var _ SystemHandler = (*systemHandler)(nil) -func NewSystemHandler(render tpl.Renderer, redis redis.IRedis, session session.ISession, biz biz.IBiz) *systemHandler { +func NewSystemHandler(render tpl.Renderer, redis redis.IRedis, session session.ISession, biz biz.IBiz, mi middleware.IMiddleware) *systemHandler { return &systemHandler{ render: render, redis: redis, session: session, biz: biz, + mi: mi, } } @@ -44,7 +47,7 @@ func (h *systemHandler) Home(w http.ResponseWriter, r *http.Request) { } func (h *systemHandler) UserHandler() UserHandler { - return NewUserHandler(h.render, h.session, h.biz) + return NewUserHandler(h.render, h.session, h.biz, h.mi) } func (h *systemHandler) MenuHandler() MenuHandler { diff --git a/internal/erpserver/handler/system/user.go b/internal/erpserver/handler/system/user.go index f7c26da..84066fc 100644 --- a/internal/erpserver/handler/system/user.go +++ b/internal/erpserver/handler/system/user.go @@ -10,16 +10,14 @@ import ( db "management/internal/db/sqlc" "management/internal/erpserver/biz" "management/internal/erpserver/model/req" - "management/internal/global" - "management/internal/global/html" - "management/internal/global/know" - "management/internal/middleware/manage/auth" "management/internal/pkg/convertor" "management/internal/pkg/crypto" + "management/internal/pkg/know" + "management/internal/pkg/middleware" "management/internal/pkg/rand" "management/internal/pkg/session" "management/internal/pkg/tpl" - "management/internal/router/manage/util" + "management/internal/pkg/tpl/html" "github.com/google/uuid" "github.com/zhang2092/browser" @@ -46,16 +44,18 @@ type userHandler struct { render tpl.Renderer session session.ISession biz biz.IBiz + mi middleware.IMiddleware } // 确保 userHandler 实现了 UserHandler 接口. var _ UserHandler = (*userHandler)(nil) -func NewUserHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz) *userHandler { +func NewUserHandler(render tpl.Renderer, session session.ISession, biz biz.IBiz, mi middleware.IMiddleware) *userHandler { return &userHandler{ render: render, session: session, biz: biz, + mi: mi, } } @@ -200,16 +200,16 @@ 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{ - "Statuses": html.NewSelectControls(global.SearchStatuses, 0), + "Statuses": html.NewSelectControls(html.SearchStatuses, "0"), }) case http.MethodPost: var q dto.SearchDto - q.SearchStatus = util.ConvertInt(r.PostFormValue("status"), 9999) + q.SearchStatus = convertor.ConvertInt(r.PostFormValue("status"), 9999) q.SearchName = r.PostFormValue("name") q.SearchEmail = r.PostFormValue("email") q.SearchID = convertor.ConvertInt[int64](r.PostFormValue("id"), 0) - q.Page = util.ConvertInt(r.PostFormValue("page"), 1) - q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10) + q.Page = convertor.ConvertInt(r.PostFormValue("page"), 1) + q.Rows = convertor.ConvertInt(r.PostFormValue("rows"), 10) res, count, err := h.biz.SystemV1().UserBiz().List(r.Context(), q) if err != nil { h.render.JSONERR(w, err.Error()) @@ -230,7 +230,7 @@ func (h *userHandler) List(w http.ResponseWriter, r *http.Request) { func (h *userHandler) Profile(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - user := auth.AuthUser(ctx) + user := h.mi.AuthUser(ctx) vm, _ := h.biz.SystemV1().UserBiz().Get(ctx, user.ID) h.render.HTML(w, r, "user/profile.tmpl", map[string]any{ "Item": vm, diff --git a/internal/erpserver/http.go b/internal/erpserver/http.go index 955ce58..3336ded 100644 --- a/internal/erpserver/http.go +++ b/internal/erpserver/http.go @@ -144,14 +144,45 @@ func NewRouter(handler handler.IHandler, mw mw.IMiddleware) *chi.Mux { // 项目 r.Route("/project", func(r chi.Router) { r.Use(mw.Authorize) - r.Get("/list", handler.PorjectHandler().List) - r.Post("/list", handler.PorjectHandler().List) - r.Get("/add", handler.PorjectHandler().Add) - r.Get("/edit", handler.PorjectHandler().Edit) - r.Post("/save", handler.PorjectHandler().Save) - // r.Post("/xmselect", projecthandler.XmSelect) - // r.Get("/dashboard", projecthandler.Dashboard) - // r.Post("/dashboard", projecthandler.PostDashboard) + r.Get("/list", handler.ProjectHandler().List) + r.Post("/list", handler.ProjectHandler().List) + r.Get("/add", handler.ProjectHandler().Add) + r.Get("/edit", handler.ProjectHandler().Edit) + r.Post("/save", handler.ProjectHandler().Save) + r.Post("/data", handler.ProjectHandler().Data) + r.Get("/dashboard", handler.ProjectHandler().Dashboard) + r.Post("/dashboard", handler.ProjectHandler().Dashboard) + }) + + // 项目预算 + r.Route("/budget", func(r chi.Router) { + r.Use(mw.Authorize) + r.Get("/list", handler.BudgetHandler().List) + r.Post("/list", handler.BudgetHandler().List) + r.Get("/add", handler.BudgetHandler().Add) + r.Get("/edit", handler.BudgetHandler().Edit) + r.Post("/save", handler.BudgetHandler().Save) + r.Post("/data", handler.BudgetHandler().Data) + }) + + // 回款单 + r.Route("/income", func(r chi.Router) { + r.Use(mw.Authorize) + r.Get("/list", handler.IncomeHandler().List) + r.Post("/list", handler.IncomeHandler().List) + r.Get("/add", handler.IncomeHandler().Add) + r.Get("/edit", handler.IncomeHandler().Edit) + r.Post("/save", handler.IncomeHandler().Save) + }) + + // 费用报销单 + r.Route("/expense", func(r chi.Router) { + r.Use(mw.Authorize) + r.Get("/list", handler.ExpenseHandler().List) + r.Post("/list", handler.ExpenseHandler().List) + r.Get("/add", handler.ExpenseHandler().Add) + r.Get("/edit", handler.ExpenseHandler().Edit) + r.Post("/save", handler.ExpenseHandler().Save) }) }) diff --git a/internal/global/auth/auth.go b/internal/global/auth/auth.go deleted file mode 100644 index 6fb323d..0000000 --- a/internal/global/auth/auth.go +++ /dev/null @@ -1,6 +0,0 @@ -package auth - -const ( - CookieName = "authorize" - StoreName = "authorize_user" -) diff --git a/internal/global/html/select.go b/internal/global/html/select.go deleted file mode 100644 index ea7c033..0000000 --- a/internal/global/html/select.go +++ /dev/null @@ -1,56 +0,0 @@ -package html - -import ( - db "management/internal/db/sqlc" - "management/internal/global" -) - -func NewSelectControls(data []*global.DataDict16, value int16) []*global.DataDict16 { - for _, item := range data { - item.Selected = false - if item.Value == value { - item.Selected = true - } - } - return data -} - -func NewSelectStringControls(data []*global.DataDict, value string) []*global.DataDict { - for _, item := range data { - item.Selected = false - if item.Value == value { - item.Selected = true - } - } - return data -} - -const ( - Course int32 = 5 - Grade int32 = 6 - Published int32 = 7 - Class int32 = 8 - Answer int32 = 80 - Getted int32 = 10 - DocumentType int32 = 45 - ValueType int32 = 54 - Period int32 = 61 -) - -func NewSelectPaperControls(data []*db.Category, value int16, Link int32) []*global.DataDict16 { - var res []*global.DataDict16 - for _, item := range data { - if item.ParentID == Link { - tmp := global.DataDict16{ - Name: item.Name, - Value: int16(item.ID), - Selected: false, - } - if int16(item.ID) == value { - tmp.Selected = true - } - res = append(res, &tmp) - } - } - return res -} diff --git a/internal/global/keys/key.go b/internal/global/keys/key.go deleted file mode 100644 index dfe4db0..0000000 --- a/internal/global/keys/key.go +++ /dev/null @@ -1,40 +0,0 @@ -package keys - -import ( - "context" - "fmt" -) - -var ( - // pear admin 配置 - PearAdmin = "m:pearjson" - - // 所有类别 - AllCategories = "m:category:all" - // 所有类别 简单信息 - AllCategorySimple = "m:categorysimple:all" - // 类别列表 根据 父id 获取 - ListCategoriesByParentID = "m:category:parent_id:%d" - - // 所有部门 - AllDepartments = "m:department:all" - - // 所有菜单 - AllMenus = "m:menus:all" - // 递归菜单 - RecursiveMenus = "m:rec_menus:%d" - // 根据用户ID获取菜单 - AdminMenus = "m:admin_menus:%d" - // 登陆用户的菜单 - OwnerMenus = "m:owner_menus:%d" - // 登陆用户的菜单 - OwnerMenusMap = "m:owner_menus_map:%d" - - // 所有角色 - AllRoles = "m:role:all" -) - -func GetManageKey(ctx context.Context, key string, arg ...any) string { - key = fmt.Sprintf(key, arg...) - return key -} diff --git a/internal/global/know/know.go b/internal/global/know/know.go deleted file mode 100644 index 1d30ebd..0000000 --- a/internal/global/know/know.go +++ /dev/null @@ -1,45 +0,0 @@ -package know - -const ( - CustomerCategory = "customer_category" - CustomerSource = "customer_source" - - BudgetCategory = "budget_category" - - IncomeBank = "income_bank" - IncomeCategory = "income_category" - - ExpenseCategory = "expense_category" - - CookieName = "authorize" - StoreName = "authorize_user" -) - -var ( - // pear admin 配置 - PearAdmin = "m:pearjson" - - // 所有类别 - AllCategories = "m:category:all" - // 所有类别 简单信息 - AllCategorySimple = "m:categorysimple:all" - // 类别列表 根据 父id 获取 - ListCategoriesByParentID = "m:category:parent_id:%d" - - // 所有部门 - AllDepartments = "m:department:all" - - // 所有菜单 - AllMenus = "m:menus:all" - // 递归菜单 - RecursiveMenus = "m:rec_menus:%d" - // 根据用户ID获取菜单 - AdminMenus = "m:admin_menus:%d" - // 登陆用户的菜单 - OwnerMenus = "m:owner_menus:%d" - // 登陆用户的菜单 - OwnerMenusMap = "m:owner_menus_map:%d" - - // 所有角色 - AllRoles = "m:role:all" -) diff --git a/internal/global/model.go b/internal/global/model.go deleted file mode 100644 index 2fdf359..0000000 --- a/internal/global/model.go +++ /dev/null @@ -1,13 +0,0 @@ -package global - -type DataDict16 struct { - Name string - Value int16 - Selected bool -} - -type DataDict struct { - Name string - Value string - Selected bool -} diff --git a/internal/global/status.go b/internal/global/status.go deleted file mode 100644 index 522ccd9..0000000 --- a/internal/global/status.go +++ /dev/null @@ -1,19 +0,0 @@ -package global - -var SearchStatuses = []*DataDict16{ - {Name: "状态", Value: 9999, Selected: false}, - {Name: "正常", Value: 0, Selected: false}, - {Name: "删除", Value: -1, Selected: false}, -} - -var Statuses = []*DataDict16{ - {Name: "正常", Value: 0, Selected: false}, - {Name: "删除", Value: -1, Selected: false}, -} - -var ProjectStatuses = []*DataDict16{ - {Name: "未开始", Value: 0, Selected: false}, - {Name: "进行中", Value: 10, Selected: false}, - {Name: "已结项", Value: 20, Selected: false}, - {Name: "已删除", Value: -1, Selected: false}, -} diff --git a/internal/global/util.go b/internal/global/util.go deleted file mode 100644 index d93e5da..0000000 --- a/internal/global/util.go +++ /dev/null @@ -1,19 +0,0 @@ -package global - -func FindItemByID(data []*DataDict16, id int16) (*DataDict16, bool) { - for _, item := range data { - if item.Value == id { - return item, true - } - } - return nil, false -} - -func FindItemByName(data []*DataDict16, name string) (*DataDict16, bool) { - for _, item := range data { - if item.Name == name { - return item, true - } - } - return nil, false -} diff --git a/internal/middleware/manage/audit/audit.go b/internal/middleware/manage/audit/audit.go deleted file mode 100644 index 5c1bc2a..0000000 --- a/internal/middleware/manage/audit/audit.go +++ /dev/null @@ -1,75 +0,0 @@ -package audit - -import ( - "context" - "net/http" - "strconv" - "strings" - "time" - - db "management/internal/db/sqlc" - "management/internal/middleware/manage/auth" - systemservice "management/internal/service/system" - - "github.com/zhang2092/browser" -) - -func Audit(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - start := time.Now() - defer func(res http.ResponseWriter, req *http.Request) { - // 记录审计日志 - go writeLog(req, start) - }(w, r) - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} - -func writeLog(req *http.Request, start time.Time) { - end := time.Now() - duration := end.Sub(start) - var params string - method := req.Method - if method == "GET" { - params = req.URL.Query().Encode() - } else if method == "POST" { - contentType := req.Header.Get("Content-Type") - if strings.Contains(contentType, "application/json") { - body := make([]byte, req.ContentLength) - req.Body.Read(body) - params = string(body) - } else if strings.Contains(contentType, "application/x-www-form-urlencoded") { - params = req.Form.Encode() - } - } - - ctx := req.Context() - au := auth.AuthUser(ctx) - arg := &db.CreateSysAuditLogParams{ - CreatedAt: time.Now(), - Email: au.Email, - Username: au.Username, - UserUuid: au.Uuid, - StartAt: start, - EndAt: end, - Duration: strconv.FormatInt(duration.Milliseconds(), 10), - Url: req.URL.RequestURI(), - Method: method, - Parameters: params, - RefererUrl: req.Header.Get("Referer"), - Ip: req.RemoteAddr, - Remark: "", - } - br, err := browser.NewBrowser(req.Header.Get("User-Agent")) - if err == nil { - arg.Os = br.Platform().Name() - arg.Browser = br.Name() - } - - c, cancel := context.WithTimeout(context.Background(), time.Second*3) - defer cancel() - - _ = systemservice.CreateSysAuditLog(c, arg) -} diff --git a/internal/middleware/manage/auth/authorize.go b/internal/middleware/manage/auth/authorize.go deleted file mode 100644 index 964a90c..0000000 --- a/internal/middleware/manage/auth/authorize.go +++ /dev/null @@ -1,80 +0,0 @@ -package auth - -import ( - "context" - "net/http" - - "management/internal/db/model/dto" - systemservice "management/internal/service/system" -) - -var defaultMenus = map[string]bool{ - "/home.html": true, - "/system/menus": true, - "/upload/img": true, - "/upload/file": true, - "/upload/mutilfile": true, - "/pear.json": true, -} - -func Authorize(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - user, ok := isLogin(ctx) - if !ok { - http.Redirect(w, r, "/", http.StatusFound) - 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 := systemservice.MapOwnerMenuByRoleID(ctx, user.RoleID) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if _, ok := menus[path]; ok { - next.ServeHTTP(w, r) - return - } - - http.Error(w, "Unauthorized", http.StatusUnauthorized) - } - - return http.HandlerFunc(fn) -} - -func isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) { - // if exists := session.Exists(ctx, auth.StoreName); exists { - // b := session.GetBytes(ctx, auth.StoreName) - // var user dto.AuthorizeUser - // if err := json.Unmarshal(b, &user); err != nil { - // return nil, false - // } - // return &user, true - // } - - return nil, false -} - -func AuthUser(ctx context.Context) dto.AuthorizeUser { - var user dto.AuthorizeUser - // if exists := session.Exists(ctx, auth.StoreName); exists { - // b := session.GetBytes(ctx, auth.StoreName) - // _ = json.Unmarshal(b, &user) - // } - return user -} diff --git a/internal/middleware/manage/nosurf/nocsrf.go b/internal/middleware/manage/nosurf/nocsrf.go deleted file mode 100644 index 7dd6c10..0000000 --- a/internal/middleware/manage/nosurf/nocsrf.go +++ /dev/null @@ -1,11 +0,0 @@ -package nosurf - -import ( - "net/http" - - "github.com/justinas/nosurf" -) - -func NoSurf(next http.Handler) http.Handler { - return nosurf.New(next) -} diff --git a/internal/middleware/manage/session/session.go b/internal/middleware/manage/session/session.go deleted file mode 100644 index 2af6705..0000000 --- a/internal/middleware/manage/session/session.go +++ /dev/null @@ -1,11 +0,0 @@ -package session - -import ( - "net/http" - - "management/internal/pkg/session" -) - -func LoadSession(session session.ISession, next http.Handler) http.Handler { - return session.LoadAndSave(next) -} diff --git a/internal/pkg/know/know.go b/internal/pkg/know/know.go index 1d30ebd..aa2b4ee 100644 --- a/internal/pkg/know/know.go +++ b/internal/pkg/know/know.go @@ -1,5 +1,10 @@ package know +import ( + "context" + "fmt" +) + const ( CustomerCategory = "customer_category" CustomerSource = "customer_source" @@ -43,3 +48,8 @@ var ( // 所有角色 AllRoles = "m:role:all" ) + +func GetManageKey(ctx context.Context, key string, arg ...any) string { + key = fmt.Sprintf(key, arg...) + return key +} diff --git a/internal/global/pearadmin/pearadmin.go b/internal/pkg/know/pearadmin/pearadmin.go similarity index 100% rename from internal/global/pearadmin/pearadmin.go rename to internal/pkg/know/pearadmin/pearadmin.go diff --git a/internal/pkg/middleware/authorize.go b/internal/pkg/middleware/authorize.go index 6fd1a5e..c78e988 100644 --- a/internal/pkg/middleware/authorize.go +++ b/internal/pkg/middleware/authorize.go @@ -6,7 +6,7 @@ import ( "net/http" "management/internal/db/model/dto" - "management/internal/global/auth" + "management/internal/pkg/know" ) var defaultMenus = map[string]bool{ @@ -59,8 +59,8 @@ func (m *middleware) Authorize(next http.Handler) http.Handler { } func (m *middleware) isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) { - if exists := m.session.Exists(ctx, auth.StoreName); exists { - b := m.session.GetBytes(ctx, auth.StoreName) + 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 @@ -73,8 +73,8 @@ func (m *middleware) isLogin(ctx context.Context) (*dto.AuthorizeUser, bool) { func (m *middleware) AuthUser(ctx context.Context) dto.AuthorizeUser { var user dto.AuthorizeUser - if exists := m.session.Exists(ctx, auth.StoreName); exists { - b := m.session.GetBytes(ctx, auth.StoreName) + if exists := m.session.Exists(ctx, know.StoreName); exists { + b := m.session.GetBytes(ctx, know.StoreName) _ = json.Unmarshal(b, &user) } return user diff --git a/internal/pkg/middleware/middleware.go b/internal/pkg/middleware/middleware.go index bc51e37..3172bdd 100644 --- a/internal/pkg/middleware/middleware.go +++ b/internal/pkg/middleware/middleware.go @@ -1,8 +1,10 @@ package middleware import ( + "context" "net/http" + "management/internal/db/model/dto" systemv1 "management/internal/erpserver/biz/v1/system" "management/internal/pkg/session" ) @@ -12,6 +14,7 @@ type IMiddleware interface { NoSurf(next http.Handler) http.Handler LoadSession(next http.Handler) http.Handler Authorize(next http.Handler) http.Handler + AuthUser(ctx context.Context) dto.AuthorizeUser } type middleware struct { diff --git a/internal/pkg/tpl/util.go b/internal/pkg/tpl/util.go index 1c7be4c..d98e6a3 100644 --- a/internal/pkg/tpl/util.go +++ b/internal/pkg/tpl/util.go @@ -13,7 +13,7 @@ import ( "strings" "management/internal/db/model/dto" - "management/internal/global/auth" + "management/internal/pkg/know" templates "management/web/templates/manage" "github.com/justinas/nosurf" @@ -25,11 +25,11 @@ func (r *render) setDefaultData(req *http.Request, data map[string]any) map[stri } ctx := req.Context() - isAuth := r.session.Exists(ctx, auth.StoreName) + isAuth := r.session.Exists(ctx, know.StoreName) data["IsAuthenticated"] = isAuth if isAuth { var authUser dto.AuthorizeUser - u := r.session.GetBytes(ctx, auth.StoreName) + u := r.session.GetBytes(ctx, know.StoreName) _ = json.Unmarshal(u, &authUser) data["AuthorizeMenus"] = r.getCurrentPathBtns(ctx, authUser.RoleID, req.URL.Path) diff --git a/internal/router/manage/budget/budget.go b/internal/router/manage/budget/budget.go deleted file mode 100644 index e87c448..0000000 --- a/internal/router/manage/budget/budget.go +++ /dev/null @@ -1,267 +0,0 @@ -package budget - -import ( - "errors" - "net/http" - "strconv" - "strings" - "time" - - "management/internal/db/model/dto" - "management/internal/db/model/form" - db "management/internal/db/sqlc" - "management/internal/global" - "management/internal/global/html" - "management/internal/global/know" - "management/internal/middleware/manage/auth" - "management/internal/pkg/convertor" - "management/internal/router/manage/util" - categoryservice "management/internal/service/category" - projectservice "management/internal/service/project" - "management/internal/tpl" - - "github.com/jackc/pgx/v5/pgtype" -) - -func List(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - pp := projectservice.AllProjects(ctx) - cc, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.BudgetCategory) - tpl.HTML(w, r, "budget/list.tmpl", map[string]any{ - "Statuses": html.NewSelectControls(global.Statuses, 0), - "Projects": html.NewSelectStringControls(pp, "0"), - "Categories": html.NewSelectStringControls(cc, "0"), - }) -} - -func PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - project := convertor.ConvertInt[int64](r.PostFormValue("project"), 9999) - if project == 0 { - project = 9999 - } - - budgetType := convertor.ConvertInt[int32](r.PostFormValue("budgetType"), 9999) - if budgetType == 0 { - budgetType = 9999 - } - - category := convertor.ConvertInt[int32](r.PostFormValue("category"), 9999) - if category == 0 { - category = 9999 - } - - title := strings.TrimSpace(r.PostFormValue("title")) - var search string - if len(title) > 0 { - search = "%" + title + "%" - if strings.HasSuffix(title, ":") { - search = title[:len(title)-1] + "%" - } - } - arg := &db.ListBudgetConditionParam{ - ProjectID: project, - BudgetType: budgetType, - Category: category, - IsTitle: len(search) > 0, - Title: search, - Status: util.ConvertInt16(r.PostFormValue("status"), 9999), - PageID: util.ConvertInt32(r.PostFormValue("page"), 1), - PageSize: util.ConvertInt32(r.PostFormValue("rows"), 10), - } - arg.TimeBegin, arg.TimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) - res, total, err := db.Engine.ListBudgetCondition(ctx, arg) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: total, - Data: res, - } - tpl.JSON(w, data) -} - -func Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "budget/edit.tmpl", map[string]any{ - "Item": &form.BudgetForm{ - BudgetType: 1, - }, - "Statuses": html.NewSelectControls(global.Statuses, 0), - }) -} - -func Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.ConvertInt[int64](vars.Get("id"), 0) - budget := &form.BudgetForm{} - ctx := r.Context() - if id > 0 { - if cus, err := db.Engine.GetBudget(ctx, id); err == nil { - budget = budget.ToForm(cus) - if u, err := db.Engine.GetSysUser(ctx, cus.CreatedUserID); err == nil { - budget.CreatedName = u.Username - } - if u, err := db.Engine.GetSysUser(ctx, cus.UpdatedUserID); err == nil { - budget.UpdatedName = u.Username - } - } - } - tpl.HTML(w, r, "budget/edit.tmpl", map[string]any{ - "Item": budget, - "Statuses": html.NewSelectControls(global.Statuses, budget.Status), - }) -} - -func Save(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - data, err := validForm(r) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - authUser := auth.AuthUser(ctx) - if data.ID > 0 { - arg := &db.UpdateBudgetParams{ - ID: data.ID, - ProjectID: pgtype.Int8{ - Int64: data.ProjectID, - Valid: true, - }, - Name: pgtype.Text{ - String: data.Name, - Valid: true, - }, - BudgetType: pgtype.Int4{ - Int32: data.BudgetType, - Valid: true, - }, - Category: pgtype.Int4{ - Int32: data.Category, - Valid: true, - }, - StartAt: pgtype.Timestamptz{ - Time: data.StartAt, - Valid: true, - }, - EndAt: pgtype.Timestamptz{ - Time: data.EndAt, - Valid: true, - }, - Amount: data.AmountF, - UsedAmount: data.UsedAmountF, - RemainingAmount: data.RemainingAmountF, - Remark: pgtype.Text{ - String: data.Remark, - Valid: true, - }, - Status: pgtype.Int2{ - Int16: data.Status, - Valid: true, - }, - UpdatedUserID: pgtype.Int4{ - Int32: authUser.ID, - Valid: true, - }, - } - _, err := db.Engine.UpdateBudget(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "更新成功") - } else { - arg := &db.CreateBudgetParams{ - ProjectID: data.ProjectID, - Name: data.Name, - BudgetType: data.BudgetType, - Category: data.Category, - StartAt: data.StartAt, - EndAt: data.EndAt, - Amount: data.AmountF, - UsedAmount: data.UsedAmountF, - RemainingAmount: data.RemainingAmountF, - Remark: data.Remark, - CreatedUserID: authUser.ID, - } - _, err := db.Engine.CreateBudget(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "添加成功") - } -} - -func XmSelect(w http.ResponseWriter, r *http.Request) { - projectID := convertor.ConvertInt[int64](r.URL.Query().Get("projectId"), 0) - all, err := db.Engine.ListBudgets(r.Context(), projectID) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - res := []*dto.XmSelectStrDto{} - for _, v := range all { - res = append(res, &dto.XmSelectStrDto{ - Name: v.Name, - Value: strconv.FormatInt(v.ID, 10), - }) - } - tpl.JSON(w, res) -} - -func validForm(r *http.Request) (form.BudgetForm, error) { - var err error - data := form.BudgetForm{} - - data.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) - data.ProjectID, err = strconv.ParseInt(r.PostFormValue("ProjectID"), 10, 64) - if err != nil || data.ProjectID == 0 { - return data, errors.New("项目不能为空") - } - - data.Name = r.PostFormValue("Name") - if len(data.Name) == 0 { - return data, errors.New("预算名称不能为空") - } - - budgetType, err := strconv.ParseInt(r.PostFormValue("BudgetType"), 10, 32) - if err != nil { - return data, errors.New("预算类型不能为空") - } - data.BudgetType = int32(budgetType) - - category, err := strconv.ParseInt(r.PostFormValue("Category"), 10, 32) - if err != nil { - return data, errors.New("预算类别不能为空") - } - data.Category = int32(category) - - data.StartAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("StartAt"), time.Local) - if err != nil { - return data, errors.New("开始时间格式错误") - } - data.EndAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("EndAt"), time.Local) - if err != nil { - return data, errors.New("结束时间格式错误") - } - - if err := data.AmountF.Scan(r.PostFormValue("Amount")); err != nil { - return data, errors.New("预算金额格式错误") - } - if err := data.UsedAmountF.Scan(r.PostFormValue("UsedAmount")); err != nil { - return data, errors.New("已支付金额格式错误") - } - if err := data.RemainingAmountF.Scan(r.PostFormValue("RemainingAmount")); err != nil { - return data, errors.New("剩余金额格式错误") - } - - data.Remark = r.PostFormValue("Remark") - data.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) - return data, nil -} diff --git a/internal/router/manage/cache/cache.go b/internal/router/manage/cache/cache.go deleted file mode 100644 index f26918b..0000000 --- a/internal/router/manage/cache/cache.go +++ /dev/null @@ -1,63 +0,0 @@ -package cache - -import ( - "net/http" - "sort" - "strings" - - "management/internal/pkg/redis" - "management/internal/router/manage/util" - "management/internal/tpl" -) - -func List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "cache/list.tmpl", nil) -} - -type Key struct { - Name string `json:"name"` -} - -func PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - keyword := strings.TrimSpace(r.PostFormValue("title")) - pageID := util.ConvertInt(r.PostFormValue("page"), 1) - pageSize := util.ConvertInt(r.PostFormValue("rows"), 10) - if len(keyword) == 0 { - keyword = "*" - } else { - keyword = keyword + "*" - } - - result, count, err := redis.ListKeys(ctx, keyword, pageID, pageSize) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - sort.Sort(sort.StringSlice(result)) - - var res []Key - for _, key := range result { - res = append(res, Key{ - Name: key, - }) - } - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: int64(count), - Data: res, - } - tpl.JSON(w, data) -} - -func Refresh(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - name := strings.TrimSpace(r.PostFormValue("name")) - err := redis.Del(ctx, name) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} diff --git a/internal/router/manage/category/category.go b/internal/router/manage/category/category.go deleted file mode 100644 index 9c5576b..0000000 --- a/internal/router/manage/category/category.go +++ /dev/null @@ -1,247 +0,0 @@ -package category - -import ( - "fmt" - "net/http" - "strconv" - "strings" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/pkg/convertor" - "management/internal/router/manage/util" - categoryservice "management/internal/service/category" - "management/internal/tpl" - - "github.com/google/uuid" - "github.com/jackc/pgx/v5/pgtype" -) - -type CategoryHandler struct{} - -func NewCategoryHandler() *CategoryHandler { - return &CategoryHandler{} -} - -func (h *CategoryHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "category/list.tmpl", nil) -} - -func (h *CategoryHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchStatus = convertor.ConvertInt(r.PostFormValue("status"), 9999) - q.SearchParentID = convertor.ConvertInt(r.PostFormValue("parentId"), 0) - q.SearchName = r.PostFormValue("name") - 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 := categoryservice.ListCategoriesCondition(r.Context(), q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} - -func (h *CategoryHandler) Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "category/edit.tmpl", map[string]any{ - "Item": &db.Category{Sort: 6666}, - }) -} - -func (h *CategoryHandler) AddChildren(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - parentID := util.DefaultInt(vars, "parentID", 0) - vm := &db.Category{ParentID: int32(parentID), Sort: 6666} - tpl.HTML(w, r, "category/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *CategoryHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - vm := &db.Category{Sort: 6666} - if id > 0 { - ctx := r.Context() - vm, _ = db.Engine.GetCategory(ctx, int32(id)) - } - tpl.HTML(w, r, "category/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *CategoryHandler) Save(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - parentID := util.ConvertInt(r.PostFormValue("ParentID"), 0) - name := r.PostFormValue("Name") - icon := r.PostFormValue("File") - if len(icon) > 0 && !strings.HasPrefix(icon, "/") { - icon = "/" + icon - } - description := r.PostFormValue("Description") - letter := r.PostFormValue("Letter") - if len(letter) == 0 { - letter = uuid.New().String() - } - sort := util.ConvertInt(r.PostFormValue("Sort"), 6666) - status := util.ConvertInt(r.PostFormValue("Status"), 9999) - - ctx := r.Context() - var parent *db.Category - if parentID > 0 { - var err error - parent, err = db.Engine.GetCategory(ctx, int32(parentID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "父级节点错误"}) - return - } - } - - path := fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID) - path = strings.ReplaceAll(path, ",,", ",") - if id == 0 { - arg := &db.CreateCategoryParams{ - Name: name, - Icon: icon, - Description: description, - Letter: letter, - ParentID: int32(parentID), - ParentPath: path, - Status: int16(status), - Sort: int32(sort), - } - _, err := db.Engine.CreateCategory(ctx, arg) - if err != nil { - if db.IsUniqueViolation(err) { - tpl.JSON(w, tpl.Response{Success: false, Message: "名称已存在"}) - return - } - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "添加成功"}) - } else { - arg := &db.UpdateCategoryParams{ - ID: int32(id), - Name: pgtype.Text{ - String: name, - Valid: true, - }, - Icon: pgtype.Text{ - String: icon, - Valid: len(icon) > 0, - }, - Description: pgtype.Text{ - String: description, - Valid: len(description) > 0, - }, - Letter: pgtype.Text{ - String: letter, - Valid: len(letter) > 0, - }, - ParentID: pgtype.Int4{ - Int32: int32(parentID), - Valid: true, - }, - ParentPath: pgtype.Text{ - String: path, - Valid: true, - }, - Sort: pgtype.Int4{ - Int32: int32(sort), - Valid: true, - }, - Status: pgtype.Int2{ - Int16: int16(status), - Valid: true, - }, - } - _, err := db.Engine.UpdateCategory(ctx, arg) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "更新成功"}) - } -} - -func (h *CategoryHandler) DTree(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - res, err := categoryservice.DTreeCategory(ctx, int32(id)) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - rsp := tpl.ResponseDtree{ - Status: tpl.ResponseDtreeStatus{ - Code: 200, - Message: "OK", - }, - Data: res, - } - tpl.JSON(w, rsp) -} - -func (h *CategoryHandler) XmSelect(w http.ResponseWriter, r *http.Request) { - letter := r.URL.Query().Get("letter") - ctx := r.Context() - if len(letter) > 0 { - all, err := categoryservice.ListByLetter(ctx, letter) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - var res []*dto.XmSelectStrDto - for _, v := range all { - res = append(res, &dto.XmSelectStrDto{ - Name: v.Name, - Value: strconv.FormatInt(int64(v.ID), 10), - }) - } - tpl.JSON(w, res) - return - } - - res, err := categoryservice.XmSelectCategory(ctx, 0) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - tpl.JSON(w, res) -} - -func (h *CategoryHandler) Refresh(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := categoryservice.RefreshCategory(ctx) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} - -func (h *CategoryHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := db.Engine.CategoryRebuildPath(ctx) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "重建成功"}) -} diff --git a/internal/router/manage/common/captcha.go b/internal/router/manage/common/captcha.go deleted file mode 100644 index 061771a..0000000 --- a/internal/router/manage/common/captcha.go +++ /dev/null @@ -1,34 +0,0 @@ -package common - -import ( - "net/http" - - "management/internal/config" - captchaservice "management/internal/service/captcha" - "management/internal/tpl" -) - -type CaptchaResponse struct { - CaptchaID string `json:"captcha_id"` - PicPath string `json:"pic_path"` - CaptchaLength int `json:"captcha_length"` - OpenCaptcha int `json:"open_captcha"` -} - -func Captcha(w http.ResponseWriter, r *http.Request) { - keyLong := config.File.Captcha.KeyLong - oc := config.File.Captcha.OpenCaptcha - id, b64s, _, err := captchaservice.Generate(config.File.Captcha.ImgHeight, config.File.Captcha.ImgWidth, keyLong, 0.7, 80) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "获取验证码失败"}) - return - } - - rsp := CaptchaResponse{ - CaptchaID: id, - PicPath: b64s, - CaptchaLength: keyLong, - OpenCaptcha: oc, - } - tpl.JSON(w, tpl.Response{Success: true, Message: "ok", Data: rsp}) -} diff --git a/internal/router/manage/common/upload.go b/internal/router/manage/common/upload.go deleted file mode 100644 index e693c7c..0000000 --- a/internal/router/manage/common/upload.go +++ /dev/null @@ -1,97 +0,0 @@ -package common - -import ( - "io" - "mime/multipart" - "net/http" - - "management/internal/tpl" - - fileutil "management/internal/pkg/file" -) - -const maxImageSize = 100 << 20 // 100 MB - -func UploadImg(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - - _, fh, err := r.FormFile("files") - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - path, err := fileutil.UploadFile(fh, fileutil.IMG) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "ok", Data: path}) -} - -func UploadFile(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - - _, fh, err := r.FormFile("files") - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - path, err := fileutil.UploadFile(fh, fileutil.ALL) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "ok", Data: path}) -} - -type UploadFileRes struct { - Name string `json:"name"` - Path string `json:"path"` -} - -func UploadMutilFiles(w http.ResponseWriter, r *http.Request) { - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(r.Body) - - err := r.ParseMultipartForm(int64(maxImageSize)) - if err != nil { - return - } - files := r.MultipartForm.File["files"] - - var res []UploadFileRes - c := make(chan UploadFileRes, 2) - defer close(c) - - for i, file := range files { - go func(item *multipart.FileHeader, key int) { - ufr := UploadFileRes{Name: item.Filename} - - filePath, err := fileutil.UploadFile(item, fileutil.ALL) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - ufr.Path = filePath - c <- ufr - }(file, i) - } - - for { - v, ok := <-c - if ok { - res = append(res, v) - } - if len(files) == len(res) { - break - } - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "ok", Data: res}) -} diff --git a/internal/router/manage/customer/customer.go b/internal/router/manage/customer/customer.go deleted file mode 100644 index fb6975a..0000000 --- a/internal/router/manage/customer/customer.go +++ /dev/null @@ -1,202 +0,0 @@ -package customer - -import ( - "net/http" - "strconv" - "strings" - - "management/internal/db/model/dto" - "management/internal/db/model/form" - db "management/internal/db/sqlc" - "management/internal/global" - "management/internal/global/html" - "management/internal/global/know" - "management/internal/middleware/manage/auth" - "management/internal/pkg/snowflake" - "management/internal/router/manage/util" - categoryservice "management/internal/service/category" - "management/internal/tpl" - - "github.com/jackc/pgx/v5/pgtype" -) - -func List(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - cc, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.CustomerCategory) - ss, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.CustomerSource) - tpl.HTML(w, r, "customer/list.tmpl", map[string]any{ - "Statuses": html.NewSelectControls(global.Statuses, 0), - "Categories": html.NewSelectStringControls(cc, "0"), - "Sources": html.NewSelectStringControls(ss, "0"), - }) -} - -func PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - category := util.ConvertInt32(r.PostFormValue("category"), 9999) - if category == 0 { - category = 9999 - } - source := util.ConvertInt32(r.PostFormValue("source"), 9999) - if source == 0 { - source = 9999 - } - - title := strings.TrimSpace(r.PostFormValue("title")) - var search string - if len(title) > 0 { - search = "%" + title + "%" - if strings.HasSuffix(title, ":") { - search = title[:len(title)-1] + "%" - } - } - arg := &db.ListCustomerConditionParam{ - IsTitle: len(search) > 0, - Title: search, - Status: util.ConvertInt16(r.PostFormValue("status"), 9999), - Category: category, - Source: source, - PageID: util.ConvertInt32(r.PostFormValue("page"), 1), - PageSize: util.ConvertInt32(r.PostFormValue("rows"), 10), - } - arg.TimeBegin, arg.TimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) - res, total, err := db.Engine.ListCustomerCondition(ctx, arg) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: total, - Data: res, - } - tpl.JSON(w, data) -} - -func Add(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - cc, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.CustomerCategory) - ss, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.CustomerSource) - tpl.HTML(w, r, "customer/edit.tmpl", map[string]any{ - "Item": &form.CustomerForm{}, - "Statuses": html.NewSelectControls(global.Statuses, 0), - "Categories": html.NewSelectStringControls(cc, "0"), - "Sources": html.NewSelectStringControls(ss, "0"), - }) -} - -func Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.ConvertInt[int64](vars.Get("id"), 0) - customer := &form.CustomerForm{} - ctx := r.Context() - if id > 0 { - if cus, err := db.Engine.GetCustomer(ctx, id); err == nil { - customer = customer.ToForm(cus) - if u, err := db.Engine.GetSysUser(ctx, cus.CreatedBy); err == nil { - customer.CreatedBy = u.Username - } - if u, err := db.Engine.GetSysUser(ctx, cus.UpdatedBy); err == nil { - customer.UpdatedBy = u.Username - } - } - } - cc, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.CustomerCategory) - ss, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.CustomerSource) - tpl.HTML(w, r, "customer/edit.tmpl", map[string]any{ - "Item": customer, - "Statuses": html.NewSelectControls(global.Statuses, customer.Status), - "Categories": html.NewSelectStringControls(cc, strconv.Itoa(int(customer.Category))), - "Sources": html.NewSelectStringControls(ss, strconv.Itoa(int(customer.Source))), - }) -} - -func Save(w http.ResponseWriter, r *http.Request) { - data := &form.CustomerForm{} - if err := form.BindForm(r, data); err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - ctx := r.Context() - authUser := auth.AuthUser(ctx) - if data.ID > 0 { - arg := &db.UpdateCustomerParams{ - ID: data.ID, - Name: pgtype.Text{ - String: data.Name, - Valid: true, - }, - Category: pgtype.Int4{ - Int32: data.Category, - Valid: true, - }, - Source: pgtype.Int4{ - Int32: data.Source, - Valid: true, - }, - Address: pgtype.Text{ - String: data.Address, - Valid: true, - }, - ContactName: pgtype.Text{ - String: data.ContactName, - Valid: true, - }, - ContactPhone: pgtype.Text{ - String: data.ContactPhone, - Valid: true, - }, - Status: pgtype.Int2{ - Int16: data.Status, - Valid: true, - }, - UpdatedBy: pgtype.Int4{ - Int32: authUser.ID, - Valid: true, - }, - } - _, err := db.Engine.UpdateCustomer(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "更新成功") - } else { - arg := &db.CreateCustomerParams{ - ID: snowflake.GetId(), - Name: data.Name, - Category: data.Category, - Source: data.Source, - Address: data.Address, - ContactName: data.ContactName, - ContactPhone: data.ContactPhone, - CreatedBy: authUser.ID, - } - _, err := db.Engine.CreateCustomer(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "添加成功") - } -} - -func XmSelect(w http.ResponseWriter, r *http.Request) { - all, err := db.Engine.AllCustomers(r.Context()) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - var res []*dto.XmSelectStrDto - for _, v := range all { - res = append(res, &dto.XmSelectStrDto{ - Name: v.Name, - Value: strconv.FormatInt(v.ID, 10), - }) - } - tpl.JSON(w, res) -} diff --git a/internal/router/manage/expense/expense.go b/internal/router/manage/expense/expense.go deleted file mode 100644 index 391abcf..0000000 --- a/internal/router/manage/expense/expense.go +++ /dev/null @@ -1,208 +0,0 @@ -package expense - -import ( - "errors" - "net/http" - "strconv" - "strings" - "time" - - "management/internal/db/model/form" - db "management/internal/db/sqlc" - "management/internal/global" - "management/internal/global/html" - "management/internal/global/know" - "management/internal/middleware/manage/auth" - "management/internal/pkg/convertor" - "management/internal/router/manage/util" - categoryservice "management/internal/service/category" - projectservice "management/internal/service/project" - "management/internal/tpl" - - "github.com/jackc/pgx/v5/pgtype" -) - -func List(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - pp := projectservice.AllProjects(ctx) - cc, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.ExpenseCategory) - tpl.HTML(w, r, "expense/list.tmpl", map[string]any{ - "Statuses": html.NewSelectControls(global.Statuses, 0), - "Projects": html.NewSelectStringControls(pp, "0"), - "Categories": html.NewSelectStringControls(cc, "0"), - }) -} - -func PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - project := convertor.ConvertInt[int64](r.PostFormValue("project"), 9999) - if project == 0 { - project = 9999 - } - budget := convertor.ConvertInt[int64](r.PostFormValue("budget"), 9999) - if budget == 0 { - budget = 9999 - } - category := convertor.ConvertInt[int32](r.PostFormValue("category"), 9999) - if category == 0 { - category = 9999 - } - - title := strings.TrimSpace(r.PostFormValue("title")) - var search string - if len(title) > 0 { - search = "%" + title + "%" - if strings.HasSuffix(title, ":") { - search = title[:len(title)-1] + "%" - } - } - arg := &db.ListExpenseConditionParam{ - ProjectID: project, - BudgetID: budget, - ExpenseType: category, - IsTitle: len(search) > 0, - Title: search, - Status: util.ConvertInt16(r.PostFormValue("status"), 9999), - PageID: util.ConvertInt32(r.PostFormValue("page"), 1), - PageSize: util.ConvertInt32(r.PostFormValue("rows"), 10), - } - arg.TimeBegin, arg.TimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) - res, total, err := db.Engine.ListExpenseCondition(ctx, arg) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: total, - Data: res, - } - tpl.JSON(w, data) -} - -func Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "expense/edit.tmpl", map[string]any{ - "Item": &form.ExpenseForm{}, - "Statuses": html.NewSelectControls(global.Statuses, 0), - }) -} - -func Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.ConvertInt[int64](vars.Get("id"), 0) - expense := &form.ExpenseForm{} - ctx := r.Context() - if id > 0 { - if cus, err := db.Engine.GetExpense(ctx, id); err == nil { - expense = expense.ToForm(cus) - if u, err := db.Engine.GetSysUser(ctx, cus.CreatedUserID); err == nil { - expense.CreatedName = u.Username - } - if u, err := db.Engine.GetSysUser(ctx, cus.UpdatedUserID); err == nil { - expense.UpdatedName = u.Username - } - } - } - tpl.HTML(w, r, "expense/edit.tmpl", map[string]any{ - "Item": expense, - "Statuses": html.NewSelectControls(global.Statuses, expense.Status), - }) -} - -func Save(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - data, err := validForm(r) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - authUser := auth.AuthUser(ctx) - if data.ID > 0 { - arg := &db.UpdateExpenseParams{ - ID: data.ID, - ProjectID: pgtype.Int8{ - Int64: data.ProjectID, - Valid: true, - }, - BudgetID: pgtype.Int8{ - Int64: data.BudgetID, - Valid: true, - }, - Amount: data.AmountF, - ExpensesAt: pgtype.Timestamptz{ - Time: data.ExpensesAt, - Valid: true, - }, - ExpensesType: pgtype.Int4{ - Int32: data.ExpensesType, - Valid: true, - }, - Remark: pgtype.Text{ - String: data.Remark, - Valid: true, - }, - Status: pgtype.Int2{ - Int16: data.Status, - Valid: true, - }, - UpdatedUserID: pgtype.Int4{ - Int32: authUser.ID, - Valid: true, - }, - } - _, err := db.Engine.UpdateExpense(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "更新成功") - } else { - arg := &db.CreateExpenseParams{ - ProjectID: data.ProjectID, - BudgetID: data.BudgetID, - Amount: data.AmountF, - ExpensesAt: data.ExpensesAt, - ExpensesType: data.ExpensesType, - Remark: data.Remark, - Status: data.Status, - CreatedUserID: authUser.ID, - } - _, err := db.Engine.CreateExpense(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "添加成功") - } -} - -func validForm(r *http.Request) (form.ExpenseForm, error) { - var err error - data := form.ExpenseForm{} - - data.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) - data.ProjectID, err = strconv.ParseInt(r.PostFormValue("ProjectID"), 10, 64) - if err != nil || data.ProjectID == 0 { - return data, errors.New("项目不能为空") - } - data.BudgetID, err = strconv.ParseInt(r.PostFormValue("BudgetID"), 10, 64) - - if err := data.AmountF.Scan(r.PostFormValue("Amount")); err != nil { - return data, errors.New("报销金额格式错误") - } - data.ExpensesAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("ExpensesAt"), time.Local) - if err != nil { - return data, errors.New("报销时间格式错误") - } - expensesType, err := strconv.ParseInt(r.PostFormValue("ExpensesType"), 10, 64) - if err != nil || expensesType == 0 { - return data, errors.New("报销类型不能为空") - } - data.ExpensesType = int32(expensesType) - - data.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) - return data, nil -} diff --git a/internal/router/manage/home.go b/internal/router/manage/home.go deleted file mode 100644 index bc09b86..0000000 --- a/internal/router/manage/home.go +++ /dev/null @@ -1,11 +0,0 @@ -package router - -import ( - "net/http" - - "management/internal/tpl" -) - -func Home(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "home/home.tmpl", nil) -} diff --git a/internal/router/manage/income/income.go b/internal/router/manage/income/income.go deleted file mode 100644 index e073654..0000000 --- a/internal/router/manage/income/income.go +++ /dev/null @@ -1,226 +0,0 @@ -package expense - -import ( - "errors" - "net/http" - "strconv" - "strings" - "time" - - "management/internal/db/model/form" - db "management/internal/db/sqlc" - "management/internal/global" - "management/internal/global/html" - "management/internal/global/know" - "management/internal/middleware/manage/auth" - "management/internal/pkg/convertor" - "management/internal/router/manage/util" - categoryservice "management/internal/service/category" - projectservice "management/internal/service/project" - "management/internal/tpl" - - "github.com/jackc/pgx/v5/pgtype" -) - -func List(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - pp := projectservice.AllProjects(ctx) - cc, _ := categoryservice.GetParentCategorySelectLetter(ctx, know.IncomeCategory) - tpl.HTML(w, r, "income/list.tmpl", map[string]any{ - "Statuses": html.NewSelectControls(global.Statuses, 0), - "Projects": html.NewSelectStringControls(pp, "0"), - "Categories": html.NewSelectStringControls(cc, "0"), - }) -} - -func PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - project := convertor.ConvertInt[int64](r.PostFormValue("project"), 9999) - if project == 0 { - project = 9999 - } - budget := convertor.ConvertInt[int64](r.PostFormValue("budget"), 9999) - if budget == 0 { - budget = 9999 - } - category := convertor.ConvertInt[int32](r.PostFormValue("category"), 9999) - if category == 0 { - category = 9999 - } - - title := strings.TrimSpace(r.PostFormValue("title")) - var search string - if len(title) > 0 { - search = "%" + title + "%" - if strings.HasSuffix(title, ":") { - search = title[:len(title)-1] + "%" - } - } - arg := &db.ListIncomeConditionParam{ - ProjectID: project, - BudgetID: budget, - IncomeType: category, - IsTitle: len(search) > 0, - Title: search, - Status: util.ConvertInt16(r.PostFormValue("status"), 9999), - PageID: util.ConvertInt32(r.PostFormValue("page"), 1), - PageSize: util.ConvertInt32(r.PostFormValue("rows"), 10), - } - arg.TimeBegin, arg.TimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) - res, total, err := db.Engine.ListIncomeCondition(ctx, arg) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: total, - Data: res, - } - tpl.JSON(w, data) -} - -func Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "income/edit.tmpl", map[string]any{ - "Item": &form.IncomeForm{}, - "Statuses": html.NewSelectControls(global.Statuses, 0), - }) -} - -func Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.ConvertInt[int64](vars.Get("id"), 0) - income := &form.IncomeForm{} - ctx := r.Context() - if id > 0 { - if cus, err := db.Engine.GetIncome(ctx, id); err == nil { - income = income.ToForm(cus) - if u, err := db.Engine.GetSysUser(ctx, cus.CreatedUserID); err == nil { - income.CreatedName = u.Username - } - if u, err := db.Engine.GetSysUser(ctx, cus.UpdatedUserID); err == nil { - income.UpdatedName = u.Username - } - } - } - tpl.HTML(w, r, "income/edit.tmpl", map[string]any{ - "Item": income, - "Statuses": html.NewSelectControls(global.Statuses, income.Status), - }) -} - -func Save(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - data, err := validForm(r) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - authUser := auth.AuthUser(ctx) - if data.ID > 0 { - arg := &db.UpdateIncomeParams{ - ID: data.ID, - ProjectID: pgtype.Int8{ - Int64: data.ProjectID, - Valid: true, - }, - BudgetID: pgtype.Int8{ - Int64: data.BudgetID, - Valid: true, - }, - Amount: data.AmountF, - IncomeAt: pgtype.Timestamptz{ - Time: data.IncomeAt, - Valid: true, - }, - IncomeType: pgtype.Int4{ - Int32: data.IncomeType, - Valid: true, - }, - IncomeBank: pgtype.Int4{ - Int32: data.IncomeBank, - Valid: true, - }, - Remark: pgtype.Text{ - String: data.Remark, - Valid: true, - }, - Status: pgtype.Int2{ - Int16: data.Status, - Valid: true, - }, - UpdatedUserID: pgtype.Int4{ - Int32: authUser.ID, - Valid: true, - }, - } - _, err := db.Engine.UpdateIncome(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "更新成功") - } else { - arg := &db.CreateIncomeParams{ - ProjectID: data.ProjectID, - BudgetID: data.BudgetID, - Amount: data.AmountF, - IncomeAt: data.IncomeAt, - IncomeType: data.IncomeType, - IncomeBank: data.IncomeBank, - Remark: data.Remark, - Status: data.Status, - CreatedUserID: authUser.ID, - } - _, err := db.Engine.CreateIncome(ctx, arg) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "添加成功") - } -} - -func validForm(r *http.Request) (form.IncomeForm, error) { - var err error - data := form.IncomeForm{} - data.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) - - data.ProjectID, err = strconv.ParseInt(r.PostFormValue("ProjectID"), 10, 64) - if err != nil || data.ProjectID == 0 { - return data, errors.New("项目不能为空") - } - - data.BudgetID, err = strconv.ParseInt(r.PostFormValue("BudgetID"), 10, 64) - if err != nil { - data.BudgetID = 0 - } - - if err := data.AmountF.Scan(r.PostFormValue("Amount")); err != nil { - return data, errors.New("报销金额格式错误") - } - - data.IncomeAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("IncomeAt"), time.Local) - if err != nil { - return data, errors.New("回款时间格式错误") - } - - incomeType, err := strconv.ParseInt(r.PostFormValue("IncomeType"), 10, 64) - if err != nil { - return data, errors.New("收入类型数据错误") - } - data.IncomeType = int32(incomeType) - - incomeBank, err := strconv.ParseInt(r.PostFormValue("IncomeBank"), 10, 64) - if err != nil { - return data, errors.New("收入银行数据错误") - } - data.IncomeBank = int32(incomeBank) - - data.Remark = r.PostFormValue("Remark") - data.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) - return data, nil -} diff --git a/internal/router/manage/oauth/oauth.go b/internal/router/manage/oauth/oauth.go deleted file mode 100644 index e95d948..0000000 --- a/internal/router/manage/oauth/oauth.go +++ /dev/null @@ -1,142 +0,0 @@ -package oauth - -import ( - "encoding/json" - "net/http" - "strings" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/pkg/crypto" - captchaservice "management/internal/service/captcha" - systemservice "management/internal/service/system" - "management/internal/tpl" - - "github.com/zhang2092/browser" -) - -func Login(w http.ResponseWriter, r *http.Request) { - // ctx := r.Context() - // var user dto.AuthorizeUser - // u := session.GetBytes(ctx, authglobal.StoreName) - // if err := json.Unmarshal(u, &user); err == nil { - // // 判断租户是否一致, 一致则刷新令牌,跳转到首页 - // if err := session.RenewToken(ctx); err == nil { - // session.Put(ctx, authglobal.StoreName, u) - // http.Redirect(w, r, "/home.html", http.StatusFound) - // return - // } - // } - - // session.Destroy(ctx) - tpl.HTML(w, r, "oauth/login.tmpl", nil) -} - -func PostLogin(w http.ResponseWriter, r *http.Request) { - email := strings.TrimSpace(r.PostFormValue("email")) - password := strings.TrimSpace(r.PostFormValue("password")) - captchaID := strings.TrimSpace(r.PostFormValue("captcha_id")) - captcha := strings.TrimSpace(r.PostFormValue("captcha")) - if len(email) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "请填写邮箱"}) - return - } - if len(password) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "请填写密码"}) - return - } - if len(captcha) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "请填写验证码"}) - return - } - if !captchaservice.Verify(captchaID, captcha, true) { - tpl.JSON(w, tpl.Response{Success: false, Message: "验证码错误"}) - return - } - - br, err := browser.NewBrowser(r.Header.Get("User-Agent")) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "平台信息获取错误"}) - return - } - - ctx := r.Context() - log := &db.CreateSysUserLoginLogParams{ - CreatedAt: time.Now(), - Email: email, - IsSuccess: false, - RefererUrl: r.Header.Get("Referer"), - Url: r.URL.RequestURI(), - Os: br.Platform().Name(), - Ip: r.RemoteAddr, - Browser: br.Name(), - } - - user, err := systemservice.GetSysUserByEmail(ctx, email) - if err != nil { - log.Message = err.Error() - _ = systemservice.CreateSysUserLoginLog(ctx, log) - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - log.UserUuid = user.Uuid - log.Username = user.Username - - err = crypto.BcryptComparePassword(user.HashedPassword, password+user.Salt) - if err != nil { - log.Message = "compare password failed" - _ = systemservice.CreateSysUserLoginLog(ctx, log) - tpl.JSON(w, tpl.Response{Success: false, Message: "compare password failed"}) - return - } - - // 登陆成功 - - if user.RoleID == 0 { - log.Message = "账号没有配置角色, 请联系管理员" - _ = systemservice.CreateSysUserLoginLog(ctx, log) - tpl.JSON(w, tpl.Response{Success: false, Message: "账号没有配置角色, 请联系管理员"}) - return - } - - sysRole, err := systemservice.GetSysRole(ctx, user.RoleID) - if err != nil { - log.Message = "账号配置的角色错误, 请联系管理员" - _ = systemservice.CreateSysUserLoginLog(ctx, log) - tpl.JSON(w, tpl.Response{Success: false, Message: "账号配置的角色错误, 请联系管理员"}) - return - } - - auth := dto.AuthorizeUser{ - ID: user.ID, - Uuid: user.Uuid, - Email: user.Email, - Username: user.Username, - RoleID: sysRole.ID, - RoleName: sysRole.Name, - OS: log.Os, - IP: log.Ip, - Browser: log.Browser, - } - - _, err = json.Marshal(auth) - if err != nil { - log.Message = err.Error() - _ = systemservice.CreateSysUserLoginLog(ctx, log) - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - // session.Put(ctx, authglobal.StoreName, b) - - log.IsSuccess = true - log.Message = "登陆成功" - _ = systemservice.CreateSysUserLoginLog(ctx, log) - tpl.JSONOK(w, "login successful") -} - -func Logout(w http.ResponseWriter, r *http.Request) { - // session.Destroy(r.Context()) - http.Redirect(w, r, "/", http.StatusFound) -} diff --git a/internal/router/manage/project/project.go b/internal/router/manage/project/project.go deleted file mode 100644 index db8de18..0000000 --- a/internal/router/manage/project/project.go +++ /dev/null @@ -1,738 +0,0 @@ -package project - -import ( - "context" - "errors" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "management/internal/db/model/dto" - formDto "management/internal/db/model/form" - "management/internal/db/model/view" - db "management/internal/db/sqlc" - "management/internal/global" - "management/internal/global/html" - "management/internal/middleware/manage/auth" - "management/internal/pkg/convertor" - "management/internal/pkg/snowflake" - "management/internal/router/manage/util" - projectservice "management/internal/service/project" - "management/internal/tpl" - - "github.com/jackc/pgx/v5/pgtype" -) - -func List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "project/list.tmpl", map[string]any{ - "Statuses": html.NewSelectControls(global.ProjectStatuses, 0), - }) -} - -func PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - title := strings.TrimSpace(r.PostFormValue("title")) - var search string - if len(title) > 0 { - search = "%" + title + "%" - if strings.HasSuffix(title, ":") { - search = title[:len(title)-1] + "%" - } - } - arg := &db.ListProjectConditionParam{ - IsTitle: len(search) > 0, - Title: search, - Status: util.ConvertInt16(r.PostFormValue("status"), 9999), - PageID: util.ConvertInt32(r.PostFormValue("page"), 1), - PageSize: util.ConvertInt32(r.PostFormValue("rows"), 10), - } - arg.TimeBegin, arg.TimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("timeBegin"), r.PostFormValue("timeEnd")) - res, total, err := db.Engine.ListProjectCondition(ctx, arg) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: total, - Data: res, - } - tpl.JSON(w, data) -} - -func Add(w http.ResponseWriter, r *http.Request) { - authUser := auth.AuthUser(r.Context()) - tpl.HTML(w, r, "project/edit.tmpl", map[string]any{ - "Item": &formDto.ProjectForm{ - ApplyUserID: authUser.ID, - ProjectFiles: &formDto.ProjectFileForm{ - ProjectFileItems: []*formDto.ProjectFileItemForm{}, - }, - }, - "Statuses": html.NewSelectControls(global.ProjectStatuses, 0), - }) -} - -func Edit(w http.ResponseWriter, r *http.Request) { - form := &formDto.ProjectForm{} - id := convertor.ConvertInt[int64](r.URL.Query().Get("id"), 0) - if id > 0 { - ctx := r.Context() - if po, err := db.Engine.GetProject(ctx, id); err == nil { - pfs, err := db.Engine.ListProjectFiles(ctx, po.ID) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - form = form.ToForm(po, pfs) - if form.ApplyUserID == 0 { - authUser := auth.AuthUser(ctx) - form.ApplyUserID = authUser.ID - } - if c, err := db.Engine.GetCustomer(ctx, po.CustomerID); err == nil { - form.CustomerName = c.Name - } - // if u, err := db.Engine.GetSysUser(ctx, po.ManagerID); err == nil { - // form.ManagerName = u.Username - // } - - // if len(po.Members) > 0 { - // arr := strings.Split(po.Members, ",") - // if len(arr) > 0 { - // var ids []int32 - // for _, v := range arr { - // ids = append(ids, convertor.ConvertInt[int32](v, 0)) - // } - - // if rows, err := db.Engine.ListSysUserByIds(ctx, ids); err == nil && len(rows) > 0 { - // log.Println("rows: ", rows) - // var names []string - // for _, v := range rows { - // names = append(names, v.Username) - // } - // form.MembersName = strings.Join(names, ",") - // } - // } - // } - - // if u, err := db.Engine.GetSysUser(ctx, po.ApplyUserID); err == nil { - // form.ApplyUserName = u.Username - // } - if u, err := db.Engine.GetSysUser(ctx, po.CreatedUserID); err == nil { - form.CreatedName = u.Username - } - if u, err := db.Engine.GetSysUser(ctx, po.UpdatedUserID); err == nil { - form.UpdatedName = u.Username - } - } - } - tpl.HTML(w, r, "project/edit.tmpl", map[string]any{ - "Item": form, - "Statuses": html.NewSelectControls(global.ProjectStatuses, form.Status), - }) -} - -func Save(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - form, err := validForm(r) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - authUser := auth.AuthUser(ctx) - if form.ID > 0 { - p := &db.UpdateProjectParams{ - ID: form.ID, - Name: pgtype.Text{ - String: form.Name, - Valid: true, - }, - StartAt: pgtype.Timestamptz{ - Time: form.StartAt, - Valid: true, - }, - EndAt: pgtype.Timestamptz{ - Time: form.EndAt, - Valid: true, - }, - CustomerID: pgtype.Int8{ - Int64: form.CustomerID, - Valid: true, - }, - TotalMoney: form.TotalMoneyF, - Description: pgtype.Text{ - String: form.Description, - Valid: true, - }, - ApplyAt: pgtype.Timestamptz{ - Time: form.ApplyAt, - Valid: true, - }, - ApplyUserID: pgtype.Int4{ - Int32: form.ApplyUserID, - Valid: true, - }, - ManagerID: pgtype.Int4{ - Int32: form.ManagerID, - Valid: true, - }, - Members: pgtype.Text{ - String: form.Members, - Valid: true, - }, - Status: pgtype.Int2{ - Int16: form.Status, - Valid: true, - }, - UpdatedUserID: pgtype.Int4{ - Int32: authUser.ID, - Valid: true, - }, - } - cpfs := []*db.CreateProjectFileParams{} - for _, pfile := range form.ProjectFiles.ProjectFileItems { - cpfs = append(cpfs, &db.CreateProjectFileParams{ - ID: snowflake.GetId(), - Name: pfile.Name, - Path: pfile.Path, - ProjectID: form.ID, - CreatedUserID: authUser.ID, - }) - } - err := projectservice.UpdateProject(ctx, p, cpfs) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "更新成功") - } else { - p := &db.CreateProjectParams{ - ID: snowflake.GetId(), - Name: form.Name, - StartAt: form.StartAt, - EndAt: form.EndAt, - CustomerID: form.CustomerID, - TotalMoney: form.TotalMoneyF, - Description: form.Description, - ApplyAt: form.ApplyAt, - ApplyUserID: form.ApplyUserID, - ManagerID: form.ManagerID, - Members: form.Members, - Status: form.Status, - CreatedUserID: authUser.ID, - } - cpfs := []*db.CreateProjectFileParams{} - for _, pfile := range form.ProjectFiles.ProjectFileItems { - cpfs = append(cpfs, &db.CreateProjectFileParams{ - ID: snowflake.GetId(), - Name: pfile.Name, - Path: pfile.Path, - ProjectID: p.ID, - CreatedUserID: authUser.ID, - }) - } - err := projectservice.CreateProject(ctx, p, cpfs) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - tpl.JSONOK(w, "添加成功") - } -} - -func XmSelect(w http.ResponseWriter, r *http.Request) { - all, err := db.Engine.AllProjects(r.Context()) - if err != nil { - tpl.JSONERR(w, err.Error()) - return - } - - var res []*dto.XmSelectStrDto - for _, v := range all { - res = append(res, &dto.XmSelectStrDto{ - Name: v.Name, - Value: strconv.FormatInt(v.ID, 10), - }) - } - tpl.JSON(w, res) -} - -func Dashboard(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "project/dashboard.tmpl", map[string]any{ - "Projects": projectservice.AllProjects(r.Context()), - }) -} - -func PostDashboard(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - t := r.PostFormValue("type") - if t == "project" { - var projectIncome float64 - var projectExpense float64 - projectID := convertor.ConvertInt[int64](r.PostFormValue("projectID"), 0) - if projectID == 0 { - si, _ := db.Engine.SumIncome(ctx) - projectIncome = convertor.NumericToFloat64(si) - se, _ := db.Engine.SumExpense(ctx) - projectExpense = convertor.NumericToFloat64(se) - } else { - pi, _ := db.Engine.SumIncomeByProjectID(ctx, projectID) - projectIncome = convertor.NumericToFloat64(pi) - pe, _ := db.Engine.SumExpenseByProjectID(ctx, projectID) - projectExpense = convertor.NumericToFloat64(pe) - } - - projectProfit := projectIncome - projectExpense - var projectProfitRate float64 = 0 - if projectExpense > 0 { - projectProfitRate = projectProfit / projectExpense - } - - res := &view.DashboardProject{ - ProjectIncome: projectIncome, - ProjectExpense: projectExpense, - ProjectProfit: projectProfit, - ProjectProfitRate: fmt.Sprintf("%0.2f%%", projectProfitRate*100), - IncomeExpenseEcharts: incomeExpenseEcharts(ctx, projectID), - IncomeEcharts: incomeEcharts(ctx, projectID), - ExpenseEcharts: expenseEcharts(ctx, projectID), - } - tpl.JSON(w, tpl.Response{Success: true, Message: "ok", Data: res}) - return - } else if t == "budget" { - } - tpl.JSONERR(w, "failed to valid type") -} - -func validForm(r *http.Request) (formDto.ProjectForm, error) { - // data := &form.ProjectForm{} - // if err := form.BindForm(r, data); err != nil { - // return data, err - // } - - // if err := data.TotalMoneyF.Scan(data.TotalMoney); err != nil { - // return data, errors.New("总金额格式错误") - // } - - // if len(data.Members) > 0 { - // membersSplit := strings.SplitSeq(data.Members, ",") - // for v := range membersSplit { - // m := convertor.ConvertInt[int32](v, 0) - // if m == 0 { - // return data, errors.New("项目成员数据错误") - // } - // } - // } - - // data.ProjectFiles = &formDto.ProjectFileForm{ - // ProjectFileItems: []*formDto.ProjectFileItemForm{}, - // } - // if len(data.Paths) > 0 { - // fnsSplit := strings.SplitSeq(data.Paths, ",") - // for v := range fnsSplit { - // if len(v) > 0 { - // read := strings.Split(v, "|") - // if len(read) != 2 { - // return data, errors.New("文件路径数据错误") - // } - // pff := &formDto.ProjectFileItemForm{ - // Name: read[0], - // Path: read[1], - // Combination: v, - // } - // data.ProjectFiles.ProjectFileItems = append(data.ProjectFiles.ProjectFileItems, pff) - // } - // } - // } - - // return data, nil - - var err error - form := formDto.ProjectForm{} - - form.ID = convertor.ConvertInt[int64](r.PostFormValue("ID"), 0) - form.CustomerID, err = strconv.ParseInt(r.PostFormValue("CustomerID"), 10, 64) - if err != nil || form.CustomerID == 0 { - return form, errors.New("客户不能为空") - } - - form.Name = r.PostFormValue("Name") - if len(form.Name) == 0 { - return form, errors.New("名称不能为空") - } - form.StartAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("StartAt"), time.Local) - if err != nil { - return form, errors.New("开始时间格式错误") - } - form.EndAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("EndAt"), time.Local) - if err != nil { - return form, errors.New("结束时间格式错误") - } - - if err := form.TotalMoneyF.Scan(r.PostFormValue("TotalMoney")); err != nil { - return form, errors.New("总金额格式错误") - } - - form.Description = r.PostFormValue("Description") - form.ApplyAt, err = time.ParseInLocation("2006-01-02", r.PostFormValue("ApplyAt"), time.Local) - if err != nil { - return form, errors.New("申请时间格式错误") - } - form.ApplyUserID = convertor.ConvertInt[int32](r.PostFormValue("ApplyUserID"), 0) - if form.ApplyUserID == 0 { - return form, errors.New("申请人不能为空") - } - - form.ManagerID = convertor.ConvertInt[int32](r.PostFormValue("ManagerID"), 0) - if form.ManagerID == 0 { - return form, errors.New("项目经理不能为空") - } - - form.Members = r.PostFormValue("Members") - if len(form.Members) > 0 { - membersSplit := strings.SplitSeq(form.Members, ",") - for v := range membersSplit { - m := convertor.ConvertInt[int32](v, 0) - if m == 0 { - return form, errors.New("项目成员数据错误") - } - } - } - - form.Status = convertor.ConvertInt[int16](r.PostFormValue("Status"), 9999) - - form.ProjectFiles = &formDto.ProjectFileForm{ - ProjectFileItems: []*formDto.ProjectFileItemForm{}, - } - fns := r.PostFormValue("Paths") - if len(fns) > 0 { - fnsSplit := strings.SplitSeq(fns, ",") - for v := range fnsSplit { - if len(v) > 0 { - read := strings.Split(v, "|") - if len(read) != 2 { - return form, errors.New("文件路径数据错误") - } - pff := &formDto.ProjectFileItemForm{ - Name: read[0], - Path: read[1], - Combination: v, - } - form.ProjectFiles.ProjectFileItems = append(form.ProjectFiles.ProjectFileItems, pff) - } - } - } - return form, nil -} - -func incomeExpenseEcharts(ctx context.Context, projectId int64) view.EchartsOption { - var name []string - var income []float64 - var expense []float64 - - if projectId == 0 { - rows, err := db.Engine.StatisticsProjects(ctx) - if err != nil || len(rows) == 0 { - return view.EchartsOption{} - } - - for _, row := range rows { - name = append(name, row.Name) - income = append(income, convertor.NumericToFloat64(row.Income)) - expense = append(expense, convertor.NumericToFloat64(row.Expense)) - } - } else { - row, err := db.Engine.StatisticsProjectItem(ctx, projectId) - if err != nil || row == nil { - return view.EchartsOption{} - } - name = append(name, row.Name) - income = append(income, convertor.NumericToFloat64(row.Income)) - expense = append(expense, convertor.NumericToFloat64(row.Expense)) - } - - return view.EchartsOption{ - Title: view.Title{ - Text: "项目收支情况", - Left: "center", - Top: 2, - FontSize: 15, - TextStyle: view.TextStyle{ - Color: "#666666", - FontWeight: "normal", - }, - }, - Color: []string{"#fed46b", "#2194ff"}, - ToolTip: view.ToolTip{ - Trigger: "axis", - AxisPointer: view.AxisPointer{ - Type: "shadow", - }, - }, - Grid: view.Grid{ - Left: "3%", - Right: "4%", - Bottom: "10%", - ContainLabel: true, - }, - Legend: view.Legend{ - Left: "center", - Top: "bottom", - Data: []string{"支出金额", "收入金额"}, - }, - XAxis: []view.XAxis{ - { - Type: "category", - Data: name, - AxisTick: view.AxisTick{ - AlignWithLabel: true, - }, - }, - }, - YAxis: []view.YAxis{ - { - Type: "value", - Name: "单位:元", - }, - }, - BarMaxWidth: "30", - Label: view.Label{ - Show: true, - Position: "top", - }, - Series: []view.Series{ - { - Name: "支出金额", - Type: "bar", - Data: expense, - ItemStyle: view.ItemStyle{ - Normal: view.Normal{ - Color: "#f89588", - }, - }, - }, - { - Name: "收入金额", - Type: "bar", - Data: income, - ItemStyle: view.ItemStyle{ - Normal: view.Normal{ - Color: "#76da91", - }, - }, - }, - }, - } -} - -func incomeEcharts(ctx context.Context, projectId int64) view.EchartsOption { - var name []string - data := []view.DataItem{} - - if projectId == 0 { - rows, err := db.Engine.StatisticsIncome(ctx) - if err != nil || len(rows) == 0 { - return view.EchartsOption{} - } - - for _, row := range rows { - name = append(name, row.IncomeTypeName) - data = append(data, view.DataItem{ - Name: row.IncomeTypeName, - Value: convertor.NumericToFloat64(row.TotalAmount), - }) - } - } else { - rows, err := db.Engine.StatisticsIncomeByProjectID(ctx, projectId) - if err != nil || len(rows) == 0 { - return view.EchartsOption{} - } - - for _, row := range rows { - name = append(name, row.IncomeTypeName) - data = append(data, view.DataItem{ - Name: row.IncomeTypeName, - Value: convertor.NumericToFloat64(row.TotalAmount), - }) - } - } - - return view.EchartsOption{ - Title: view.Title{ - Text: "收入分布", - Left: "center", - Orient: "vertical", - FontSize: 15, - TextStyle: view.TextStyle{ - Color: "#666666", - FontWeight: "normal", - }, - }, - ToolTip: view.ToolTip{ - Trigger: "item", - }, - Legend: view.Legend{ - Left: "center", - Top: "bottom", - Data: name, - }, - Color: []string{"#63b2ee", "#76da91", "#f8cb7f", "#f89588", "#7cd6cf", "#9192ab", "#7898e1", "#efa666", "#eddd86", "#9987ce", "#63b2ee", "#76da91"}, - Series: []view.Series{ - { - Type: "pie", - Radius: "50%", - Data: data, - Label: view.Label{ - Show: true, - TextStyle: view.TextStyle{ - Color: "#666666", - }, - Normal: view.LableNormal{ - Formatter: "{c} ({d}%)", - TextStyle: view.TextStyle{ - Color: "#666666", - FontWeight: "normal", - }, - }, - }, - }, - }, - } -} - -func expenseEcharts(ctx context.Context, projectId int64) view.EchartsOption { - var name []string - data := []view.DataItem{} - - if projectId == 0 { - rows, err := db.Engine.StatisticsExpense(ctx) - if err != nil || len(rows) == 0 { - return view.EchartsOption{} - } - - for _, row := range rows { - name = append(name, row.ExpensesTypeName) - data = append(data, view.DataItem{ - Name: row.ExpensesTypeName, - Value: convertor.NumericToFloat64(row.TotalAmount), - }) - } - } else { - rows, err := db.Engine.StatisticsExpenseByProjectID(ctx, projectId) - if err != nil || len(rows) == 0 { - return view.EchartsOption{} - } - - for _, row := range rows { - name = append(name, row.ExpensesTypeName) - data = append(data, view.DataItem{ - Name: row.ExpensesTypeName, - Value: convertor.NumericToFloat64(row.TotalAmount), - }) - } - } - - return view.EchartsOption{ - Title: view.Title{ - Text: "支出分布", - Left: "center", - Orient: "vertical", - FontSize: 15, - TextStyle: view.TextStyle{ - Color: "#666666", - FontWeight: "normal", - }, - }, - ToolTip: view.ToolTip{ - Trigger: "item", - }, - Legend: view.Legend{ - Left: "center", - Top: "bottom", - Data: name, - }, - Color: []string{"#63b2ee", "#76da91", "#f8cb7f", "#f89588", "#7cd6cf", "#9192ab", "#7898e1", "#efa666", "#eddd86", "#9987ce", "#63b2ee", "#76da91"}, - Series: []view.Series{ - { - Type: "pie", - Radius: "50%", - Data: data, - Label: view.Label{ - Show: true, - TextStyle: view.TextStyle{ - Color: "#666666", - }, - Normal: view.LableNormal{ - Formatter: "{c} ({d}%)", - TextStyle: view.TextStyle{ - Color: "#666666", - FontWeight: "normal", - }, - }, - }, - }, - }, - } - - // return view.EchartsOption{ - // Title: view.Title{ - // Text: "支出分布", - // Left: "center", - // Top: 2, - // FontSize: 15, - // TextStyle: view.TextStyle{ - // Color: "#666666", - // FontWeight: "normal", - // }, - // }, - // Color: []string{"#fed46b", "#2194ff"}, - // ToolTip: view.ToolTip{ - // Trigger: "axis", - // AxisPointer: view.AxisPointer{ - // Type: "shadow", - // }, - // }, - // Grid: view.Grid{ - // Left: "3%", - // Right: "4%", - // Bottom: "10%", - // ContainLabel: true, - // }, - // XAxis: []view.XAxis{ - // { - // Type: "category", - // Data: name, - // AxisTick: view.AxisTick{ - // AlignWithLabel: true, - // }, - // }, - // }, - // YAxis: []view.YAxis{ - // { - // Type: "value", - // Name: "单位:元", - // }, - // }, - // BarMaxWidth: "30", - // Label: view.Label{ - // Show: true, - // Position: "top", - // }, - // Series: []view.Series{ - // { - // Type: "bar", - // Data: expense, - // ItemStyle: view.ItemStyle{ - // Normal: view.Normal{ - // Color: "#f89588", - // }, - // }, - // }, - // }, - // } -} diff --git a/internal/router/manage/router.go b/internal/router/manage/router.go deleted file mode 100644 index 1c6fcbf..0000000 --- a/internal/router/manage/router.go +++ /dev/null @@ -1,226 +0,0 @@ -package router - -import ( - "net/http" - - "management/internal/middleware/manage/audit" - "management/internal/middleware/manage/auth" - "management/internal/middleware/manage/nosurf" - - budgethandler "management/internal/router/manage/budget" - cachehandler "management/internal/router/manage/cache" - categoryhandler "management/internal/router/manage/category" - commonhandler "management/internal/router/manage/common" - customerhandler "management/internal/router/manage/customer" - expensehandler "management/internal/router/manage/expense" - incomehandler "management/internal/router/manage/income" - oauthhandler "management/internal/router/manage/oauth" - projecthandler "management/internal/router/manage/project" - systemhandler "management/internal/router/manage/system" - - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" -) - -func NewRouter() *chi.Mux { - r := chi.NewRouter() - - r.Use(middleware.RequestID) - r.Use(middleware.RealIP) - // r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - - staticServer := http.FileServer(http.Dir("./web/statics/")) - r.Handle("/statics/*", http.StripPrefix("/statics", staticServer)) - - uploadServer := http.FileServer(http.Dir("./upload/")) - r.Handle("/upload/*", http.StripPrefix("/upload", uploadServer)) - - r.Group(func(r chi.Router) { - r.Use(nosurf.NoSurf) // CSRF - // r.Use(session.LoadSession) // Session - - r.Get("/captcha", commonhandler.Captcha) - - r.Get("/", oauthhandler.Login) - r.Post("/login", oauthhandler.PostLogin) - r.Get("/logout", oauthhandler.Logout) - - r.With(auth.Authorize, audit.Audit).Post("/upload/img", commonhandler.UploadImg) - r.With(auth.Authorize, audit.Audit).Post("/upload/file", commonhandler.UploadFile) - r.With(auth.Authorize, audit.Audit).Post("/upload/mutilfile", commonhandler.UploadMutilFiles) - - r.With(auth.Authorize, audit.Audit).Get("/home.html", Home) - - configHandler := systemhandler.NewSysConfigHandler() - r.With(auth.Authorize).Get("/pear.json", configHandler.Pear) - - r.Route("/system", func(r chi.Router) { - r.Use(auth.Authorize) - - r.Route("/config", func(r chi.Router) { - r.Use(audit.Audit) - r.Get("/list", configHandler.List) - r.Post("/list", configHandler.PostList) - r.Get("/add", configHandler.Add) - r.Get("/edit", configHandler.Edit) - r.Post("/save", configHandler.Save) - r.Post("/reset_pear", configHandler.ResetPear) - r.Post("/refresh", configHandler.Refresh) - }) - - r.Route("/department", func(r chi.Router) { - r.Use(audit.Audit) - departHandler := systemhandler.NewSysDepartmentHandler() - r.Get("/list", departHandler.List) - r.Post("/list", departHandler.PostList) - r.Get("/add", departHandler.Add) - r.Get("/add_children", departHandler.AddChildren) - r.Get("/edit", departHandler.Edit) - r.Post("/save", departHandler.Save) - r.Post("/dtree", departHandler.DTree) - r.Post("/refresh", departHandler.Refresh) - r.Post("/rebuild_parent_path", departHandler.RebuildParentPath) - }) - - r.Route("/user", func(r chi.Router) { - r.Use(audit.Audit) - userHandler := systemhandler.NewSysUserHandler() - r.Get("/list", userHandler.List) - r.Post("/list", userHandler.PostList) - r.Get("/add", userHandler.Add) - r.Get("/edit", userHandler.Edit) - r.Post("/save", userHandler.Save) - r.Get("/profile", userHandler.Profile) - r.Post("/xmselect", userHandler.XmSelect) - }) - - r.Route("/login_log", func(r chi.Router) { - // r.Use(audit.Audit) - userLoginLogHandler := systemhandler.NewSysUserLoginLogHandler() - r.Get("/list", userLoginLogHandler.List) - r.Post("/list", userLoginLogHandler.PostList) - }) - - r.Route("/audit_log", func(r chi.Router) { - // r.Use(audit.Audit) - userAuditLogHandler := systemhandler.NewSysAuditLogHandler() - r.Get("/list", userAuditLogHandler.List) - r.Post("/list", userAuditLogHandler.PostList) - }) - - r.Route("/role", func(r chi.Router) { - r.Use(audit.Audit) - roleHandler := systemhandler.NewSysRoleHandler() - r.Get("/list", roleHandler.List) - r.Post("/list", roleHandler.PostList) - r.Get("/add", roleHandler.Add) - r.Get("/edit", roleHandler.Edit) - r.Post("/save", roleHandler.Save) - r.Post("/dtree", roleHandler.DTree) - r.Post("/refresh", roleHandler.Refresh) - r.Post("/rebuild_parent_path", roleHandler.RebuildParentPath) - r.Post("/refresh_role_menus", roleHandler.RefreshRoleMenus) - r.Post("/xm_select", roleHandler.XmSelect) - r.Get("/set_menu", roleHandler.SetMenu) - r.Post("/set_menu", roleHandler.PostSetMenu) - }) - - menuHandler := systemhandler.NewSysMenuHandler() - r.Get("/menus", menuHandler.UserMenus) - r.Route("/menu", func(r chi.Router) { - r.Use(audit.Audit) - r.Get("/tree", menuHandler.Tree) - r.Get("/list", menuHandler.List) - r.Post("/list", menuHandler.PostList) - r.Get("/add", menuHandler.Add) - r.Get("/add_children", menuHandler.AddChildren) - r.Get("/edit", menuHandler.Edit) - r.Post("/save", menuHandler.Save) - r.Post("/xm_select_tree", menuHandler.XmSelectTree) - r.Post("/refresh_cache", menuHandler.Refresh) - }) - - // 类别 - r.Route("/category", func(r chi.Router) { - r.Use(audit.Audit) - categoryHandler := categoryhandler.NewCategoryHandler() - r.Get("/list", categoryHandler.List) - r.Post("/list", categoryHandler.PostList) - r.Get("/add", categoryHandler.Add) - r.Get("/add_children", categoryHandler.AddChildren) - r.Get("/edit", categoryHandler.Edit) - r.Post("/save", categoryHandler.Save) - r.Post("/dtree", categoryHandler.DTree) - r.Post("/xmselect", categoryHandler.XmSelect) - r.Post("/refresh", categoryHandler.Refresh) - r.Post("/rebuild_parent_path", categoryHandler.RebuildParentPath) - }) - }) - - // 客户 - r.Route("/customer", func(r chi.Router) { - r.Use(auth.Authorize) - r.Get("/list", customerhandler.List) - r.Post("/list", customerhandler.PostList) - r.Get("/add", customerhandler.Add) - r.Get("/edit", customerhandler.Edit) - r.Post("/save", customerhandler.Save) - r.Post("/xmselect", customerhandler.XmSelect) - }) - - // 项目 - r.Route("/project", func(r chi.Router) { - r.Use(auth.Authorize) - r.Get("/list", projecthandler.List) - r.Post("/list", projecthandler.PostList) - r.Get("/add", projecthandler.Add) - r.Get("/edit", projecthandler.Edit) - r.Post("/save", projecthandler.Save) - r.Post("/xmselect", projecthandler.XmSelect) - r.Get("/dashboard", projecthandler.Dashboard) - r.Post("/dashboard", projecthandler.PostDashboard) - }) - - // 项目预算 - r.Route("/budget", func(r chi.Router) { - r.Use(auth.Authorize) - r.Get("/list", budgethandler.List) - r.Post("/list", budgethandler.PostList) - r.Get("/add", budgethandler.Add) - r.Get("/edit", budgethandler.Edit) - r.Post("/save", budgethandler.Save) - r.Post("/xmselect", budgethandler.XmSelect) - }) - - // 回款单 - r.Route("/income", func(r chi.Router) { - r.Use(auth.Authorize) - r.Get("/list", incomehandler.List) - r.Post("/list", incomehandler.PostList) - r.Get("/add", incomehandler.Add) - r.Get("/edit", incomehandler.Edit) - r.Post("/save", incomehandler.Save) - }) - - // 费用报销单 - r.Route("/expense", func(r chi.Router) { - r.Use(auth.Authorize) - r.Get("/list", expensehandler.List) - r.Post("/list", expensehandler.PostList) - r.Get("/add", expensehandler.Add) - r.Get("/edit", expensehandler.Edit) - r.Post("/save", expensehandler.Save) - }) - - // 缓存 - r.Route("/cache", func(r chi.Router) { - r.Use(auth.Authorize) - r.Get("/list", cachehandler.List) - r.Post("/list", cachehandler.PostList) - r.Post("/refresh", cachehandler.Refresh) - }) - }) - - return r -} diff --git a/internal/router/manage/system/sys_audit_log.go b/internal/router/manage/system/sys_audit_log.go deleted file mode 100644 index 841a736..0000000 --- a/internal/router/manage/system/sys_audit_log.go +++ /dev/null @@ -1,43 +0,0 @@ -package system - -import ( - "net/http" - - "management/internal/db/model/dto" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" -) - -type SysAuditLogHandler struct{} - -func NewSysAuditLogHandler() *SysAuditLogHandler { - return &SysAuditLogHandler{} -} - -func (h *SysAuditLogHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "audit_log/list.tmpl", nil) -} - -func (h *SysAuditLogHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchTimeBegin, q.SearchTimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("SearchTimeBegin"), r.PostFormValue("SearchTimeEnd")) - q.SearchName = r.PostFormValue("SearchName") - q.SearchKey = r.PostFormValue("SearchKey") - q.Page = util.ConvertInt(r.PostFormValue("page"), 1) - q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10) - ctx := r.Context() - res, count, err := systemservice.ListSysAuditLog(ctx, q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} diff --git a/internal/router/manage/system/sys_config.go b/internal/router/manage/system/sys_config.go deleted file mode 100644 index 0449c1d..0000000 --- a/internal/router/manage/system/sys_config.go +++ /dev/null @@ -1,166 +0,0 @@ -package system - -import ( - "encoding/json" - "net/http" - "strings" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/global/pearadmin" - "management/internal/pkg/redis" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" -) - -type SysConfigHandler struct{} - -func NewSysConfigHandler() *SysConfigHandler { - return &SysConfigHandler{} -} - -func (h *SysConfigHandler) Pear(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - pear, err := systemservice.PearConfig(ctx) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - tpl.JSON(w, pear) -} - -func (h *SysConfigHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "config/list.tmpl", nil) -} - -func (h *SysConfigHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchKey = r.PostFormValue("SearchKey") - q.Page = util.ConvertInt(r.PostFormValue("page"), 1) - q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10) - ctx := r.Context() - res, count, err := systemservice.ListSysConfigCondition(ctx, q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} - -type EditSysConfig struct { - *db.SysConfig - Result string -} - -func (h *SysConfigHandler) Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "config/edit.tmpl", map[string]any{ - "Item": &db.SysConfig{}, - "Result": "", - }) -} - -func (h *SysConfigHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - vm := &EditSysConfig{} - if id > 0 { - ctx := r.Context() - if conf, err := systemservice.GetSysConfig(ctx, int32(id)); err == nil { - vm.SysConfig = conf - vm.Result = string(conf.Value) - } - } - tpl.HTML(w, r, "config/edit.tmpl", map[string]any{ - "Item": vm.SysConfig, - "Result": vm.Result, - }) -} - -func (h *SysConfigHandler) Save(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - key := r.PostFormValue("Key") - value := r.PostFormValue("Value") - if len(key) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "Key不能为空"}) - return - } - if len(value) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "Value不能为空"}) - return - } - - ctx := r.Context() - if id == 0 { - arg := &db.CreateSysConfigParams{ - Key: key, - Value: []byte(value), - } - err := systemservice.CreateSysConfig(ctx, arg) - if err != nil { - if db.IsUniqueViolation(err) { - tpl.JSON(w, tpl.Response{Success: false, Message: "数据已存在"}) - return - } - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "添加成功"}) - } else { - res, err := systemservice.GetSysConfig(ctx, int32(id)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - arg := &db.UpdateSysConfigByKeyParams{ - Key: res.Key, - Value: []byte(value), - } - err = systemservice.UpdateSysConfigByKey(ctx, arg) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "更新成功"}) - } -} - -func (h *SysConfigHandler) ResetPear(w http.ResponseWriter, r *http.Request) { - b, err := json.Marshal(pearadmin.PearJson) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - ctx := r.Context() - err = systemservice.UpdateSysConfigByKey(ctx, &db.UpdateSysConfigByKeyParams{ - Key: pearadmin.PearKey, - Value: b, - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - tpl.JSON(w, tpl.Response{Success: true, Message: "重置成功"}) -} - -func (h *SysConfigHandler) Refresh(w http.ResponseWriter, r *http.Request) { - key := r.FormValue("key") - err := redis.Del(r.Context(), strings.ToLower(key)) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} diff --git a/internal/router/manage/system/sys_department.go b/internal/router/manage/system/sys_department.go deleted file mode 100644 index 0ecd412..0000000 --- a/internal/router/manage/system/sys_department.go +++ /dev/null @@ -1,177 +0,0 @@ -package system - -import ( - "fmt" - "net/http" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" -) - -type SysDepartmentHandler struct{} - -func NewSysDepartmentHandler() *SysDepartmentHandler { - return &SysDepartmentHandler{} -} - -func (h *SysDepartmentHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "department/list.tmpl", nil) -} - -func (h *SysDepartmentHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchStatus = util.ConvertInt(r.PostFormValue("SearchStatus"), 9999) - q.SearchParentID = util.ConvertInt(r.PostFormValue("SearchParentID"), 0) - q.SearchName = r.PostFormValue("SearchName") - q.SearchKey = r.PostFormValue("SearchKey") - q.Page = util.ConvertInt(r.PostFormValue("page"), 1) - q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10) - res, count, err := systemservice.ListSysDepartmentCondition(r.Context(), q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} - -func (h *SysDepartmentHandler) Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "department/edit.tmpl", map[string]any{ - "Item": &db.SysDepartment{Sort: 6666}, - }) -} - -func (h *SysDepartmentHandler) AddChildren(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - parentID := util.DefaultInt(vars, "parentID", 0) - vm := &db.SysDepartment{ParentID: int32(parentID), Sort: 6666} - tpl.HTML(w, r, "department/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysDepartmentHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - vm := &db.SysDepartment{Sort: 6666} - if id > 0 { - vm, _ = systemservice.GetSysDepartment(r.Context(), int32(id)) - } - tpl.HTML(w, r, "department/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysDepartmentHandler) Save(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - ParentID := util.ConvertInt(r.PostFormValue("ParentID"), 0) - name := r.PostFormValue("Name") - sort := util.ConvertInt(r.PostFormValue("Sort"), 6666) - status := util.ConvertInt(r.PostFormValue("Status"), 9999) - - ctx := r.Context() - var parent *db.SysDepartment - if ParentID > 0 { - var err error - parent, err = systemservice.GetSysDepartment(ctx, int32(ParentID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "父级节点错误"}) - return - } - } - - if id == 0 { - arg := db.CreateSysDepartmentParams{ - Name: name, - ParentID: int32(ParentID), - ParentPath: fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID), - Status: int32(status), - Sort: int32(sort), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - _, err := systemservice.CreateSysDepartment(ctx, &arg) - if err != nil { - if db.IsUniqueViolation(err) { - tpl.JSON(w, tpl.Response{Success: false, Message: "部门名称已存在"}) - return - } - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "添加成功"}) - } else { - res, err := systemservice.GetSysDepartment(ctx, int32(id)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - arg := &db.UpdateSysDepartmentParams{ - ID: res.ID, - Name: name, - ParentID: int32(ParentID), - ParentPath: fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID), - Status: int32(status), - Sort: int32(sort), - UpdatedAt: time.Now(), - } - _, err = systemservice.UpdateSysDepartment(ctx, arg) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "更新成功"}) - } -} - -func (h *SysDepartmentHandler) DTree(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - res, err := systemservice.DTreeSysDepartment(ctx, 0) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - rsp := tpl.ResponseDtree{ - Status: tpl.ResponseDtreeStatus{ - Code: 200, - Message: "OK", - }, - Data: res, - } - tpl.JSON(w, rsp) -} - -func (h *SysDepartmentHandler) Refresh(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := systemservice.RefreshSysDepartment(ctx) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} - -func (h *SysDepartmentHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := systemservice.RebuildSysDepartmentParentPath(ctx) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "重建成功"}) -} diff --git a/internal/router/manage/system/sys_menu.go b/internal/router/manage/system/sys_menu.go deleted file mode 100644 index bc07ccd..0000000 --- a/internal/router/manage/system/sys_menu.go +++ /dev/null @@ -1,223 +0,0 @@ -package system - -import ( - "net/http" - "strconv" - "strings" - "time" - - db "management/internal/db/sqlc" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" - - "github.com/google/uuid" -) - -type SysMenuHandler struct{} - -func NewSysMenuHandler() *SysMenuHandler { - return &SysMenuHandler{} -} - -func (h *SysMenuHandler) Tree(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - res, err := systemservice.ToTreeMenu(ctx, id, true) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - tpl.JSON(w, res) -} - -func (h *SysMenuHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "menu/list.tmpl", nil) -} - -func (h *SysMenuHandler) PostList(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - res, err := systemservice.ListMenuTree(ctx) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: 0, - Data: res, - } - tpl.JSON(w, data) -} - -func (h *SysMenuHandler) Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "menu/edit.tmpl", map[string]any{ - "Item": &db.SysMenu{Style: "pear-btn-primary pear-btn-sm", Visible: true, Sort: 6666}, - }) -} - -func (h *SysMenuHandler) AddChildren(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - parentID := util.DefaultInt(vars, "parentID", 0) - vm := &db.SysMenu{ParentID: int32(parentID), Style: "pear-btn-primary pear-btn-sm", Visible: true, Sort: 6666} - ctx := r.Context() - parent, err := systemservice.GetSysMenu(ctx, int32(parentID)) - if err == nil { - if parent.Type == "node" { - vm.Type = "menu" - } else if parent.Type == "menu" { - vm.Type = "btn" - } - } - tpl.HTML(w, r, "menu/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysMenuHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - vm := &db.SysMenu{Style: "pear-btn-primary pear-btn-sm", Sort: 6666} - if id > 0 { - ctx := r.Context() - vm, _ = systemservice.GetSysMenu(ctx, int32(id)) - } - tpl.HTML(w, r, "menu/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysMenuHandler) Save(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - name := r.PostFormValue("Name") - dispalyName := r.PostFormValue("DisplayName") - t := r.PostFormValue("Type") - url := r.PostFormValue("Url") - if len(url) == 0 { - url = uuid.Must(uuid.NewRandom()).String() - } - avatar := r.PostFormValue("Avatar") - style := r.PostFormValue("Style") - parentID := util.ConvertInt(r.PostFormValue("ParentID"), 0) - visible := util.ConvertBool(r.PostFormValue("Visible"), false) - isList := util.ConvertBool(r.PostFormValue("IsList"), false) - sort := util.ConvertInt(r.PostFormValue("Sort"), 6666) - status := util.ConvertInt(r.PostFormValue("Status"), 9999) - - ctx := r.Context() - if len(avatar) > 0 && !strings.Contains(avatar, "layui-icon ") { - avatar = "layui-icon " + avatar - } - - parentPath := "" - if parentID > 0 { - parent, err := systemservice.GetSysMenu(ctx, int32(parentID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - parentPath = parent.ParentPath + "," + strconv.Itoa(parentID) + "," - parentPath = strings.ReplaceAll(parentPath, ",,", ",") - } - - if id == 0 { - arg := &db.CreateSysMenuParams{ - Name: name, - DisplayName: dispalyName, - Url: url, - Type: t, - ParentID: int32(parentID), - ParentPath: parentPath, - Avatar: avatar, - Style: style, - Visible: visible, - IsList: isList, - Status: int32(status), - Sort: int32(sort), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - _, err := systemservice.CreateSysMenu(ctx, arg) - if err != nil { - if db.IsUniqueViolation(err) { - tpl.JSON(w, tpl.Response{Success: false, Message: "菜单已存在"}) - return - } - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "添加成功"}) - } else { - res, err := systemservice.GetSysMenu(ctx, int32(id)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - arg := &db.UpdateSysMenuParams{ - ID: res.ID, - Name: name, - DisplayName: dispalyName, - Url: url, - Type: t, - ParentID: int32(parentID), - ParentPath: parentPath, - Avatar: avatar, - Style: style, - Visible: visible, - IsList: isList, - Status: int32(status), - Sort: int32(sort), - UpdatedAt: time.Now(), - } - _, err = systemservice.UpdateSysMenu(ctx, arg) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "更新成功"}) - } -} - -func (h *SysMenuHandler) UserMenus(w http.ResponseWriter, r *http.Request) { - // ctx := r.Context() - // b := session.GetBytes(ctx, auth.StoreName) - // var u dto.AuthorizeUser - // if err := json.Unmarshal(b, &u); err != nil { - // http.Error(w, err.Error(), http.StatusInternalServerError) - // return - // } - // menus, err := systemservice.RecursiveSysMenus(ctx, u.RoleID) - // if err != nil { - // tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - // return - // } - - tpl.JSON(w, nil) -} - -func (h *SysMenuHandler) XmSelectTree(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - res, err := systemservice.ToTreeMenu(ctx, 0, false) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - tpl.JSON(w, res) -} - -func (h *SysMenuHandler) Refresh(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := systemservice.RefreshMenus(ctx) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} diff --git a/internal/router/manage/system/sys_role.go b/internal/router/manage/system/sys_role.go deleted file mode 100644 index ad99603..0000000 --- a/internal/router/manage/system/sys_role.go +++ /dev/null @@ -1,300 +0,0 @@ -package system - -import ( - "fmt" - "net/http" - "strings" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/pkg/convertor" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" -) - -type SysRoleHandler struct{} - -func NewSysRoleHandler() *SysRoleHandler { - return &SysRoleHandler{} -} - -func (h *SysRoleHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "role/list.tmpl", nil) -} - -func (h *SysRoleHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchStatus = convertor.ConvertInt(r.PostFormValue("status"), 9999) - q.SearchParentID = convertor.ConvertInt(r.PostFormValue("parentId"), 0) - q.SearchName = r.PostFormValue("name") - 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 := systemservice.ListSysRoleCondition(r.Context(), q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} - -func (h *SysRoleHandler) Add(w http.ResponseWriter, r *http.Request) { - vm := &db.SysRole{Sort: 6666} - tpl.HTML(w, r, "role/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysRoleHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - vm := &db.SysRole{Sort: 6666} - if id > 0 { - ctx := r.Context() - vm, _ = systemservice.GetSysRole(ctx, int32(id)) - } - tpl.HTML(w, r, "role/edit.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysRoleHandler) Save(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - name := r.PostFormValue("Name") - parentID := util.ConvertInt(r.PostFormValue("ParentID"), 0) - displayName := r.PostFormValue("DisplayName") - sort := util.ConvertInt(r.PostFormValue("Sort"), 6666) - status := util.ConvertInt(r.PostFormValue("Status"), 9999) - - ctx := r.Context() - var parent *db.SysRole - if parentID > 0 { - var err error - parent, err = systemservice.GetSysRole(ctx, int32(parentID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "父级节点错误"}) - return - } - } else { - parent = &db.SysRole{ - ID: 0, - ParentID: 0, - ParentPath: ",0,", - } - } - - if id == 0 { - arg := &db.CreateSysRoleParams{ - Name: name, - DisplayName: displayName, - Vip: false, - ParentID: parent.ID, - ParentPath: fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID), - Status: int32(status), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - _, err := systemservice.CreateSysRole(ctx, arg) - if err != nil { - if db.IsUniqueViolation(err) { - tpl.JSON(w, tpl.Response{Success: false, Message: "角色名称已存在"}) - return - } - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "添加成功"}) - } else { - res, err := systemservice.GetSysRole(ctx, int32(id)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - arg := &db.UpdateSysRoleParams{ - ID: res.ID, - DisplayName: displayName, - Sort: int32(sort), - Status: int32(status), - ParentID: parent.ID, - ParentPath: fmt.Sprintf("%s,%d,", parent.ParentPath, parent.ID), - UpdatedAt: time.Now(), - } - _, err = systemservice.UpdateSysRole(ctx, arg) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "更新成功"}) - } -} - -func (h *SysRoleHandler) XmSelect(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - res, err := systemservice.XmSelectSysRole(ctx, 0) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - tpl.JSON(w, res) -} - -func (h *SysRoleHandler) SetMenu(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - vm := struct { - Role *db.SysRole - Menus []*dto.SetMenuDto - }{} - if id > 0 { - ctx := r.Context() - var err error - vm.Role, err = systemservice.GetSysRole(ctx, int32(id)) - if err == nil { - vm.Menus, _ = systemservice.SetMenuViewData(ctx, vm.Role.ID) - } - } - - tpl.HTML(w, r, "role/set_menu.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysRoleHandler) PostSetMenu(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - menus := r.PostFormValue("roleMenu") - - if id == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "角色异常, 请刷新重试"}) - return - } - - if len(menus) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "请选择菜单"}) - return - } - - ctx := r.Context() - _, err := systemservice.GetSysRole(ctx, int32(id)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - menuArr := strings.Split(menus, ",") - if len(menuArr) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "请选择菜单"}) - return - } - - var menuList []*db.SysMenu - for _, v := range menuArr { - menuID := util.ConvertInt(v, 0) - if menuID > 0 { - menu, err := systemservice.GetSysMenu(ctx, int32(menuID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - menuList = append(menuList, menu) - } - } - - if len(menuList) == 0 { - tpl.JSON(w, tpl.Response{Success: false, Message: "请选择正确的菜单"}) - return - } - - err = systemservice.SetMenu(ctx, int32(id), menuList) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "设置成功"}) -} - -func (h *SysRoleHandler) DTree(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - res, err := systemservice.DTreeSysRole(ctx, 0) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - rsp := tpl.ResponseDtree{ - Status: tpl.ResponseDtreeStatus{ - Code: 200, - Message: "OK", - }, - Data: res, - } - tpl.JSON(w, rsp) -} - -func (h *SysRoleHandler) Refresh(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := systemservice.RefreshSysRole(ctx) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} - -func (h *SysRoleHandler) RebuildParentPath(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - err := systemservice.RebuildSysRoleParentPath(ctx) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "重建成功"}) -} - -func (h *SysRoleHandler) RefreshRoleMenus(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - // 获取需要刷新的角色ID - roleID := util.ConvertInt(r.PostFormValue("roleID"), 0) - sysRole, err := systemservice.GetSysRole(ctx, int32(roleID)) - if err != nil || sysRole == nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - // 刷新角色菜单 (角色所拥有的菜单集合) - _, err = systemservice.SetOwnerListMenuByRoleID(ctx, sysRole.ID) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - // 刷新角色菜单 (角色所拥有的菜单集合) - _, err = systemservice.SetOwnerMapMenuByRoleID(ctx, sysRole.ID) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - // 刷新角色菜单树 (pear admin layui 使用的格式) - _, err = systemservice.SetRecursiveSysMenus(ctx, sysRole.ID) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "刷新成功"}) -} diff --git a/internal/router/manage/system/sys_user.go b/internal/router/manage/system/sys_user.go deleted file mode 100644 index 5a60b23..0000000 --- a/internal/router/manage/system/sys_user.go +++ /dev/null @@ -1,213 +0,0 @@ -package system - -import ( - "net/http" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/middleware/manage/auth" - "management/internal/pkg/crypto" - "management/internal/pkg/rand" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" - - "github.com/google/uuid" -) - -type SysUserHandler struct{} - -func NewSysUserHandler() *SysUserHandler { - return &SysUserHandler{} -} - -func (h *SysUserHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "user/list.tmpl", nil) -} - -func (h *SysUserHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchStatus = util.ConvertInt(r.PostFormValue("SearchStatus"), 9999) - q.SearchName = r.PostFormValue("SearchName") - q.SearchKey = r.PostFormValue("SearchKey") - q.Page = util.ConvertInt(r.PostFormValue("page"), 1) - q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10) - ctx := r.Context() - res, count, err := systemservice.ListSysUserCondition(ctx, q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} - -func (h *SysUserHandler) Add(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "user/edit.tmpl", map[string]any{ - "Item": &db.SysUser{ - HashedPassword: nil, - }, - }) -} - -func (h *SysUserHandler) Edit(w http.ResponseWriter, r *http.Request) { - vars := r.URL.Query() - id := util.DefaultInt(vars, "id", 0) - sysUser := &db.SysUser{} - if id > 0 { - ctx := r.Context() - if user, err := systemservice.GetSysUser(ctx, int32(id)); err == nil { - user.HashedPassword = nil - sysUser = user - } - } - tpl.HTML(w, r, "user/edit.tmpl", map[string]any{ - "Item": sysUser, - }) -} - -func (h *SysUserHandler) Profile(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - user := auth.AuthUser(ctx) - vm, _ := systemservice.GetSysUser(ctx, user.ID) - tpl.HTML(w, r, "user/profile.tmpl", map[string]any{ - "Item": vm, - }) -} - -func (h *SysUserHandler) Save(w http.ResponseWriter, r *http.Request) { - id := util.ConvertInt(r.PostFormValue("ID"), 0) - email := r.PostFormValue("Email") - username := r.PostFormValue("Username") - password := r.PostFormValue("Password") - changePassword := r.PostFormValue("ChangePassword") - gender := util.ConvertInt(r.PostFormValue("Gender"), 0) - avatar := r.PostFormValue("File") - status := util.ConvertInt(r.PostFormValue("Status"), 9999) - - ctx := r.Context() - departmentID := util.ConvertInt(r.PostFormValue("DepartmentID"), 0) - var department *db.SysDepartment - var err error - if departmentID > 0 { - department, err = systemservice.GetSysDepartment(ctx, int32(departmentID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "部门数据错误"}) - return - } - } - var role *db.SysRole - roleID := util.ConvertInt(r.PostFormValue("RoleID"), 0) - if roleID > 0 { - role, err = systemservice.GetSysRole(ctx, int32(roleID)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: "角色数据错误"}) - return - } - } - - if id == 0 { - salt, err := rand.String(10) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - hashedPassword, err := crypto.BcryptHashPassword(password + salt) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - initTime, err := time.ParseInLocation(time.DateTime, "0001-01-01 00:00:00", time.Local) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - arg := &db.CreateSysUserParams{ - Uuid: uuid.Must(uuid.NewV7()), - Email: email, - Username: username, - HashedPassword: hashedPassword, - Salt: salt, - Avatar: avatar, - Gender: int32(gender), - DepartmentID: department.ID, - RoleID: role.ID, - Status: int32(status), - ChangePasswordAt: initTime, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - _, err = systemservice.CreateSysUser(ctx, arg) - if err != nil { - if db.IsUniqueViolation(err) { - tpl.JSON(w, tpl.Response{Success: false, Message: "数据已存在"}) - return - } - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "添加成功"}) - } else { - res, err := systemservice.GetSysUser(ctx, int32(id)) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - arg := &db.UpdateSysUserParams{ - ID: res.ID, - Username: username, - HashedPassword: res.HashedPassword, - Avatar: avatar, - Gender: int32(gender), - DepartmentID: department.ID, - RoleID: role.ID, - Status: int32(status), - ChangePasswordAt: res.ChangePasswordAt, - UpdatedAt: time.Now(), - } - if changePassword == "on" { - hashedPassword, err := crypto.BcryptHashPassword(password + res.Salt) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - arg.HashedPassword = hashedPassword - arg.ChangePasswordAt = time.Now() - } - _, err = systemservice.UpdateSysUser(ctx, arg) - if err != nil { - tpl.JSON(w, tpl.Response{Success: false, Message: err.Error()}) - return - } - - tpl.JSON(w, tpl.Response{Success: true, Message: "更新成功"}) - } -} - -func (h *SysUserHandler) XmSelect(w http.ResponseWriter, r *http.Request) { - all, err := db.Engine.ListSysUser(r.Context()) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - var res []*dto.XmSelectInt32Dto - for _, v := range all { - res = append(res, &dto.XmSelectInt32Dto{ - Name: v.Username, - Value: v.ID, - }) - } - tpl.JSON(w, res) -} diff --git a/internal/router/manage/system/sys_user_login_log.go b/internal/router/manage/system/sys_user_login_log.go deleted file mode 100644 index e6dacaf..0000000 --- a/internal/router/manage/system/sys_user_login_log.go +++ /dev/null @@ -1,43 +0,0 @@ -package system - -import ( - "net/http" - - "management/internal/db/model/dto" - "management/internal/router/manage/util" - systemservice "management/internal/service/system" - "management/internal/tpl" -) - -type SysUserLoginLogHandler struct{} - -func NewSysUserLoginLogHandler() *SysUserLoginLogHandler { - return &SysUserLoginLogHandler{} -} - -func (h *SysUserLoginLogHandler) List(w http.ResponseWriter, r *http.Request) { - tpl.HTML(w, r, "login_log/list.tmpl", nil) -} - -func (h *SysUserLoginLogHandler) PostList(w http.ResponseWriter, r *http.Request) { - var q dto.SearchDto - q.SearchTimeBegin, q.SearchTimeEnd = util.DefaultStartTimeAndEndTime(r.PostFormValue("SearchTimeBegin"), r.PostFormValue("SearchTimeEnd")) - q.SearchName = r.PostFormValue("SearchName") - q.SearchKey = r.PostFormValue("SearchKey") - q.Page = util.ConvertInt(r.PostFormValue("page"), 1) - q.Rows = util.ConvertInt(r.PostFormValue("rows"), 10) - ctx := r.Context() - res, count, err := systemservice.ListSysUserLoginLog(ctx, q) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - data := tpl.ResponseList{ - Code: 0, - Message: "ok", - Count: count, - Data: res, - } - tpl.JSON(w, data) -} diff --git a/internal/router/manage/util/util.go b/internal/router/manage/util/util.go deleted file mode 100644 index 99ea2b3..0000000 --- a/internal/router/manage/util/util.go +++ /dev/null @@ -1,83 +0,0 @@ -package util - -import ( - "net/url" - "strconv" - "time" - - "github.com/jackc/pgx/v5/pgtype" -) - -func DefaultStartTimeAndEndTime(start string, end string) (string, string) { - if len(start) == 0 { - start = "2000-01-01 00:00:00" - } - if len(end) == 0 { - end = time.Now().Add(time.Hour * 24).Format(time.DateTime) - } - return start, end -} - -func DefaultString(values url.Values, key, defaultValue string) string { - v := values.Get(key) - if len(v) == 0 { - return defaultValue - } - - return v -} - -func DefaultInt(values url.Values, key string, defaultValue int) int { - v := values.Get(key) - if len(v) == 0 { - return defaultValue - } - - i, err := strconv.Atoi(v) - if err != nil { - return defaultValue - } - - return i -} - -func ConvertInt16(value string, defaultValue int16) int16 { - i, err := strconv.Atoi(value) - if err != nil { - return defaultValue - } - - return int16(i) -} - -func ConvertInt32(value string, defaultValue int32) int32 { - i, err := strconv.Atoi(value) - if err != nil { - return defaultValue - } - - return int32(i) -} - -func ConvertBool(value string, defaultValue bool) bool { - b, err := strconv.ParseBool(value) - if err != nil { - return defaultValue - } - - return b -} - -func ConvertInt[T int | int16 | int32 | int64](value string, defaultValue T) T { - i, err := strconv.Atoi(value) - if err != nil { - return defaultValue - } - return T(i) -} - -func PgtypeNumericToFloat64(num pgtype.Numeric) float64 { - f1, _ := num.Float64Value() - f2, _ := f1.Value() - return f2.(float64) -} diff --git a/internal/service/aliyunoss/aliyunoss.go b/internal/service/aliyunoss/aliyunoss.go deleted file mode 100644 index c2e695d..0000000 --- a/internal/service/aliyunoss/aliyunoss.go +++ /dev/null @@ -1,82 +0,0 @@ -package aliyunoss - -import ( - "bytes" - "errors" - "fmt" - "io" - "mime/multipart" - "time" - - "management/internal/config" - - "github.com/aliyun/aliyun-oss-go-sdk/oss" - "github.com/h2non/filetype" - gonanoid "github.com/matoous/go-nanoid/v2" -) - -var engine *oss.Client - -var AllowImageMaxSize int64 = 10485760 - -func Init() error { - var err error - engine, err = oss.New(config.File.AliyunUpload.Endpoint, config.File.AliyunUpload.AccessKeyID, config.File.AliyunUpload.AccessKeySecret, oss.Timeout(10, 120)) - if err != nil { - return err - } - return err -} - -func UploadImage(file *multipart.FileHeader) (string, error) { - if file.Size > AllowImageMaxSize { - return "", errors.New("failed to receive image too large") - } - - fileOpen, err := file.Open() - if err != nil { - return "", errors.New("failed to image open") - } - defer func(fileOpen multipart.File) { - _ = fileOpen.Close() - }(fileOpen) - - fileBytes, err := io.ReadAll(fileOpen) - if err != nil { - return "", errors.New("failed to read image") - } - - if !filetype.IsImage(fileBytes) { - return "", errors.New("failed to no image type") - } - - kind, err := filetype.Match(fileBytes) - if err != nil || kind == filetype.Unknown { - return "", errors.New("failed to get image type") - } - - imgPath := GenerateFilename(kind.Extension) - err = PutObject(imgPath, fileBytes) - if err != nil { - return "", err - } - return imgPath, nil -} - -func GenerateFilename(extension string) string { - id, _ := gonanoid.New() - return fmt.Sprintf("upload/%s/%s/%s/%s.%s", time.Now().Format("2006"), time.Now().Format("01"), time.Now().Format("02"), id, extension) -} - -func PutObject(path string, stream []byte) error { - bucket, err := engine.Bucket(config.File.AliyunUpload.Bucket) - if err != nil { - return errors.New("failed to get bucket") - } - - err = bucket.PutObject(path, bytes.NewReader(stream)) - if err != nil { - return errors.New("failed to upload to oss") - } - return nil -} diff --git a/internal/service/applet/applet.go b/internal/service/applet/applet.go deleted file mode 100644 index 525e215..0000000 --- a/internal/service/applet/applet.go +++ /dev/null @@ -1,81 +0,0 @@ -package applet - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "time" - - "management/internal/config" - "management/internal/pkg/fetcher" - "management/internal/pkg/redis" -) - -// var appletLoginErrs = map[int]string{ -// -1: "系统繁忙", -// 40029: "js_code无效", -// 45011: "API调用太频繁,请稍候再试", -// 40226: "高风险等级用户,小程序登录拦截", -// } - -type AppletLoginResponse struct { - OpenID string `json:"openid"` - SessionKey string `json:"session_key"` - UnionID string `json:"unionid"` - ErrCode int `json:"errcode"` - ErrMsg string `json:"errmsg"` -} - -func AppletLogin(code string) (*AppletLoginResponse, error) { - url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", - config.File.Applet.AppID, config.File.Applet.AppSecret, code) - response, status, err := fetcher.Get(url, time.Second*3) - if err != nil { - return nil, err - } - - if status != 200 { - return nil, errors.New("请求失败") - } - - var res AppletLoginResponse - err = json.Unmarshal(response, &res) - if err != nil { - return nil, err - } - return &res, nil -} - -type accessTokenResponse struct { - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` -} - -func GetAccessToken(ctx context.Context) (string, error) { - key := "token:" + config.File.Applet.AppID - token, err := redis.Get(ctx, key) - if err == nil && len(token) > 0 { - return token, nil - } - - url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", - config.File.Applet.AppID, config.File.Applet.AppSecret) - response, status, err := fetcher.Get(url, time.Second*3) - if err != nil { - return "", err - } - - if status != 200 { - return "", errors.New("获取access token失败") - } - - var res accessTokenResponse - err = json.Unmarshal(response, &res) - if err != nil { - return "", err - } - - redis.Set(ctx, key, res.AccessToken, time.Duration(res.ExpiresIn-100)*time.Second) - return res.AccessToken, nil -} diff --git a/internal/service/budget/budget.go b/internal/service/budget/budget.go deleted file mode 100644 index 69e1703..0000000 --- a/internal/service/budget/budget.go +++ /dev/null @@ -1,31 +0,0 @@ -package budget - -import ( - "context" - "strconv" - - db "management/internal/db/sqlc" - "management/internal/global" -) - -func AllBudgets(ctx context.Context, projectId int64) []*global.DataDict { - pp, err := db.Engine.ListBudgets(ctx, projectId) - if err != nil || len(pp) == 0 { - return nil - } - - var res []*global.DataDict - res = append(res, &global.DataDict{ - Name: "请选择", - Value: "0", - }) - for _, v := range pp { - item := global.DataDict{ - Name: v.Name, - Value: strconv.Itoa(int(v.ID)), - } - res = append(res, &item) - } - - return res -} diff --git a/internal/service/captcha/captcha.go b/internal/service/captcha/captcha.go deleted file mode 100644 index 295c3ef..0000000 --- a/internal/service/captcha/captcha.go +++ /dev/null @@ -1,18 +0,0 @@ -package captcha - -import "github.com/mojocn/base64Captcha" - -var captchaStore base64Captcha.Store = base64Captcha.DefaultMemStore - -func 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) - // driver := base64Captcha.NewDriverString(config.File.Captcha.ImgHeight, - // config.File.Captcha.ImgWidth, - // 6, 1, keyLong, source, nil, nil, nil) - cp := base64Captcha.NewCaptcha(driver, captchaStore) - return cp.Generate() -} - -func Verify(id, answer string, clear bool) bool { - return captchaStore.Verify(id, answer, clear) -} diff --git a/internal/service/category/category.go b/internal/service/category/category.go deleted file mode 100644 index 82bd69e..0000000 --- a/internal/service/category/category.go +++ /dev/null @@ -1,340 +0,0 @@ -package category - -import ( - "context" - "encoding/json" - "errors" - "strconv" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/global" - "management/internal/global/keys" - "management/internal/pkg/redis" -) - -func ListCategoriesCondition(ctx context.Context, q dto.SearchDto) ([]*db.Category, int64, error) { - countArg := &db.CountCategoriesConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int16(q.SearchStatus), - IsID: q.SearchID != 0, - ID: int32(q.SearchID), - IsParentID: q.SearchParentID != 0 && q.SearchParentID != 1, - ParentID: int32(q.SearchParentID), - Name: q.SearchName, - } - - dataArg := &db.ListCategoriesConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int16(q.SearchStatus), - IsID: q.SearchID != 0, - ID: int32(q.SearchID), - IsParentID: q.SearchParentID != 0 && q.SearchParentID != 1, - ParentID: int32(q.SearchParentID), - Name: q.SearchName, - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - } - - count, err := db.Engine.CountCategoriesCondition(ctx, countArg) - if err != nil { - return nil, 0, err - } - - categories, err := db.Engine.ListCategoriesCondition(ctx, dataArg) - if err != nil { - return nil, 0, err - } - - return categories, count, nil -} - -func DTreeCategory(ctx context.Context, id int32) ([]*dto.DTreeDto, error) { - all, err := db.Engine.AllCategories(ctx) - if err != nil { - return nil, err - } - - return toDtree(id, all), nil -} - -func XmSelectCategory(ctx context.Context, id int32) ([]*dto.XmSelectTreeDto, error) { - all, err := db.Engine.AllCategories(ctx) - if err != nil { - return nil, err - } - - return toXmSelectTree(id, all), nil -} - -func GetParentCategorySelectLetter(ctx context.Context, letter string) ([]*global.DataDict, error) { - all := AllCategories(ctx) - if len(all) == 0 { - return nil, errors.New("请刷新类别缓存") - } - - var current *db.Category - for _, v := range all { - if v.Letter == letter { - current = v - break - } - } - if current == nil { - return nil, errors.New("未找到当前类别") - } - - var res []*global.DataDict - res = append(res, &global.DataDict{ - Name: "请选择", - Value: "0", - }) - for _, v := range all { - if v.ParentID == current.ID { - item := global.DataDict{ - Name: v.Name, - Value: strconv.Itoa(int(v.ID)), - } - res = append(res, &item) - } - } - - return res, nil -} - -func ListByParentID(ctx context.Context, parentID int32) ([]*db.Category, error) { - all := AllCategories(ctx) - if len(all) == 0 { - return nil, errors.New("请刷新类别缓存") - } - var res []*db.Category - for _, v := range all { - if v.ParentID == parentID { - res = append(res, v) - } - } - return res, nil -} - -func ListByLetter(ctx context.Context, letter string) ([]*db.Category, error) { - all := AllCategories(ctx) - if len(all) == 0 { - return nil, errors.New("请刷新类别缓存") - } - var current *db.Category - for _, v := range all { - if v.Letter == letter { - current = v - break - } - } - if current == nil { - return nil, errors.New("未找到当前类别") - } - - var res []*db.Category - for _, v := range all { - if v.ParentID == current.ID { - res = append(res, v) - } - } - return res, nil -} - -func GetParentCategorySelect(ctx context.Context, id int32) ([]*global.DataDict, error) { - all := AllCategories(ctx) - if len(all) == 0 { - return nil, errors.New("请刷新类别缓存") - } - - var res []*global.DataDict - res = append(res, &global.DataDict{ - Name: "请选择", - Value: "0", - }) - for _, v := range all { - if v.ParentID == id { - item := global.DataDict{ - Name: v.Name, - Value: strconv.Itoa(int(v.ID)), - } - res = append(res, &item) - } - } - - return res, nil -} - -func toDtree(parentId int32, data []*db.Category) []*dto.DTreeDto { - var res []*dto.DTreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.DTreeDto{} - item.ID = strconv.FormatInt(int64(v.ID), 10) - item.Title = v.Name - item.Last = !hasChildren(v.ID, data) - item.ParentId = strconv.FormatInt(int64(v.ParentID), 10) - item.Children = toDtree(v.ID, data) - if v.ParentID == 0 { - item.Spread = true - } - res = append(res, &item) - } - } - - return res -} - -func toXmSelectTree(parentId int32, data []*db.Category) []*dto.XmSelectTreeDto { - var res []*dto.XmSelectTreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.XmSelectTreeDto{ - Name: v.Name, - Value: strconv.FormatInt(int64(v.ID), 10), - Children: toXmSelectTree(v.ID, data), - } - res = append(res, &item) - } - } - - return res -} - -func hasChildren(parentId int32, data []*db.Category) bool { - if len(data) > 0 { - for _, v := range data { - if v.ParentID == parentId { - return true - } - } - } - - return false -} - -func RefreshCategory(ctx context.Context) error { - all, err := db.Engine.AllCategories(ctx) - if err != nil { - return err - } - - b, err := json.Marshal(all) - if err != nil { - return err - } - - redis.Del(ctx, keys.GetManageKey(ctx, keys.AllCategorySimple)) - key := keys.GetManageKey(ctx, keys.AllCategories) - err = redis.Set(ctx, key, b, time.Hour*6) - return err -} - -func AllCategories(ctx context.Context) []*db.Category { - var res []*db.Category - key := keys.GetManageKey(ctx, keys.AllCategories) - b, err := redis.GetBytes(ctx, key) - if err == nil { - if err := json.Unmarshal(b, &res); err == nil { - return res - } - } - - res, err = db.Engine.AllCategories(ctx) - if err == nil { - if b, err = json.Marshal(res); err == nil { - redis.Set(ctx, key, b, time.Hour*6) - } - } - - return res -} - -type CategorySet struct { - Title string `json:"title"` - Data []*CategoryItem `json:"data"` -} - -type CategoryItem struct { - ID int32 `json:"id"` - Name string `json:"name"` - Icon string `json:"icon"` - Description string `json:"description"` - Url string `json:"url"` -} - -func AllCategoriesItem(ctx context.Context) []*CategoryItem { - var res []*CategoryItem - key := keys.GetManageKey(ctx, keys.AllCategorySimple) - b, err := redis.GetBytes(ctx, key) - if err == nil { - if err = json.Unmarshal(b, &res); err == nil { - return res - } - } - all, err := db.Engine.AllCategories(ctx) - if err != nil { - return nil - } - for _, v := range all { - res = append(res, &CategoryItem{ - ID: v.ID, - Name: v.Name, - }) - } - b, err = json.Marshal(res) - if err == nil { - redis.Set(ctx, key, b, time.Hour*6) - } - return res -} - -func ListCategoriesByParentID(ctx context.Context, parentID int) ([]*CategorySet, error) { - var res []*CategorySet - key := keys.GetManageKey(ctx, keys.ListCategoriesByParentID, parentID) - b, err := redis.GetBytes(ctx, key) - if err == nil { - if err = json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - all, err := db.Engine.ListCategoriesByPath(ctx, "%,"+strconv.Itoa(parentID)+",%") - if err != nil { - return nil, err - } - - root := findCategoryItem(parentID, all) - for _, v := range root { - children := findCategoryItem(int(v.ID), all) - if len(children) > 0 { - res = append(res, &CategorySet{ - Title: v.Name, - Data: children, - }) - } - } - - b, err = json.Marshal(res) - if err == nil { - redis.Set(ctx, key, b, time.Hour*6) - } - - return res, nil -} - -func findCategoryItem(parentID int, all []*db.Category) []*CategoryItem { - var res []*CategoryItem - for _, v := range all { - if v.ParentID == int32(parentID) { - item := CategoryItem{} - item.ID = v.ID - item.Name = v.Name - item.Icon = v.Icon - item.Description = v.Description - res = append(res, &item) - } - } - return res -} diff --git a/internal/service/project/project.go b/internal/service/project/project.go deleted file mode 100644 index ff460bb..0000000 --- a/internal/service/project/project.go +++ /dev/null @@ -1,72 +0,0 @@ -package project - -import ( - "context" - "strconv" - - db "management/internal/db/sqlc" - "management/internal/global" -) - -func CreateProject(ctx context.Context, p *db.CreateProjectParams, pf []*db.CreateProjectFileParams) error { - return db.Engine.ExecTx(ctx, func(q *db.Queries) error { - _, err := q.CreateProject(ctx, p) - if err != nil { - return err - } - - for _, item := range pf { - _, err = q.CreateProjectFile(ctx, item) - if err != nil { - return err - } - } - - return nil - }) -} - -func UpdateProject(ctx context.Context, p *db.UpdateProjectParams, pf []*db.CreateProjectFileParams) error { - return db.Engine.ExecTx(ctx, func(q *db.Queries) error { - _, err := q.UpdateProject(ctx, p) - if err != nil { - return err - } - - err = q.DeleteProjectFile(ctx, p.ID) - if err != nil { - return err - } - - for _, item := range pf { - _, err = q.CreateProjectFile(ctx, item) - if err != nil { - return err - } - } - - return nil - }) -} - -func AllProjects(ctx context.Context) []*global.DataDict { - pp, err := db.Engine.AllProjects(ctx) - if err != nil || len(pp) == 0 { - return nil - } - - var res []*global.DataDict - res = append(res, &global.DataDict{ - Name: "请选择", - Value: "0", - }) - for _, v := range pp { - item := global.DataDict{ - Name: v.Name, - Value: strconv.Itoa(int(v.ID)), - } - res = append(res, &item) - } - - return res -} diff --git a/internal/service/system/auth.go b/internal/service/system/auth.go deleted file mode 100644 index 9b140a3..0000000 --- a/internal/service/system/auth.go +++ /dev/null @@ -1 +0,0 @@ -package system diff --git a/internal/service/system/sys_audit_log.go b/internal/service/system/sys_audit_log.go deleted file mode 100644 index e49b3ff..0000000 --- a/internal/service/system/sys_audit_log.go +++ /dev/null @@ -1,59 +0,0 @@ -package system - -import ( - "context" - "strings" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" -) - -func CreateSysAuditLog(ctx context.Context, arg *db.CreateSysAuditLogParams) error { - return db.Engine.CreateSysAuditLog(ctx, arg) -} - -func ListSysAuditLog(ctx context.Context, q dto.SearchDto) ([]*db.SysAuditLog, int64, error) { - start, err := time.ParseInLocation(time.DateTime, q.SearchTimeBegin, time.Local) - if err != nil { - return nil, 0, err - } - end, err := time.ParseInLocation(time.DateTime, q.SearchTimeEnd, time.Local) - if err != nil { - return nil, 0, err - } - - countArg := &db.CountSysAuditLogConditionParams{ - StartAt: start, - EndAt: end, - } - - dataArg := &db.ListSysAuditLogConditionParams{ - StartAt: start, - EndAt: end, - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - } - - if len(q.SearchKey) > 0 { - switch strings.ToLower(q.SearchName) { - case "email": - countArg.Email = q.SearchKey - dataArg.Email = q.SearchKey - case "username": - countArg.Username = q.SearchKey - dataArg.Username = q.SearchKey - } - } - count, err := db.Engine.CountSysAuditLogCondition(ctx, countArg) - if err != nil { - return nil, 0, err - } - - audits, err := db.Engine.ListSysAuditLogCondition(ctx, dataArg) - if err != nil { - return nil, 0, err - } - - return audits, count, nil -} diff --git a/internal/service/system/sys_config.go b/internal/service/system/sys_config.go deleted file mode 100644 index bfb2cf2..0000000 --- a/internal/service/system/sys_config.go +++ /dev/null @@ -1,72 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/global/keys" - "management/internal/global/pearadmin" - "management/internal/pkg/redis" -) - -func CreateSysConfig(ctx context.Context, arg *db.CreateSysConfigParams) error { - return db.Engine.CreateSysConfig(ctx, arg) -} - -func UpdateSysConfigByKey(ctx context.Context, arg *db.UpdateSysConfigByKeyParams) error { - return db.Engine.UpdateSysConfigByKey(ctx, arg) -} - -func GetSysConfig(ctx context.Context, id int32) (*db.SysConfig, error) { - return db.Engine.GetSysConfig(ctx, id) -} - -func GetSysConfigByKey(ctx context.Context, key string) (*db.SysConfig, error) { - return db.Engine.GetSysConfigByKey(ctx, key) -} - -func PearConfig(ctx context.Context) (*dto.PearConfig, error) { - // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.PearAdmin) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res *dto.PearConfig - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - conf, err := db.Engine.GetSysConfigByKey(ctx, pearadmin.PearKey) - if err != nil { - return nil, err - } - - var pear dto.PearConfig - if err := json.Unmarshal(conf.Value, &pear); err != nil { - return nil, err - } - - _ = redis.Set(ctx, key, conf.Value, time.Hour*6) - return &pear, nil -} - -func ListSysConfigCondition(ctx context.Context, q dto.SearchDto) ([]*db.SysConfig, int64, error) { - count, err := db.Engine.CountSysConfigCondition(ctx, q.SearchKey) - if err != nil { - return nil, 0, err - } - - configs, err := db.Engine.ListSysConfigCondition(ctx, &db.ListSysConfigConditionParams{ - Key: q.SearchKey, - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - }) - if err != nil { - return nil, 0, err - } - - return configs, count, nil -} diff --git a/internal/service/system/sys_department.go b/internal/service/system/sys_department.go deleted file mode 100644 index 95021eb..0000000 --- a/internal/service/system/sys_department.go +++ /dev/null @@ -1,232 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "strconv" - "strings" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/global/keys" - "management/internal/pkg/redis" -) - -func CreateSysDepartment(ctx context.Context, arg *db.CreateSysDepartmentParams) (*db.SysDepartment, error) { - return db.Engine.CreateSysDepartment(ctx, arg) -} - -func UpdateSysDepartment(ctx context.Context, arg *db.UpdateSysDepartmentParams) (*db.SysDepartment, error) { - return db.Engine.UpdateSysDepartment(ctx, arg) -} - -func GetSysDepartment(ctx context.Context, id int32) (*db.SysDepartment, error) { - return db.Engine.GetSysDepartment(ctx, id) -} - -func AllCache(ctx context.Context) ([]*db.SysDepartment, error) { - key := keys.GetManageKey(ctx, keys.AllDepartments) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res []*db.SysDepartment - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - all, err := db.Engine.AllSysDepartment(ctx) - if err != nil { - return nil, err - } - - b, err = json.Marshal(all) - if err != nil { - return nil, err - } - - _ = redis.Set(ctx, key, b, time.Hour*6) - return all, nil -} - -func ListSysDepartmentCondition(ctx context.Context, q dto.SearchDto) ([]*db.SysDepartment, int64, error) { - countArg := &db.CountSysDepartmentConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int32(q.SearchStatus), - IsParentID: q.SearchParentID != 0, - ParentID: int32(q.SearchParentID), - } - - dataArg := &db.ListSysDepartmentConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int32(q.SearchStatus), - IsParentID: q.SearchParentID != 0, - ParentID: int32(q.SearchParentID), - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - } - - if len(q.SearchKey) > 0 { - switch strings.ToLower(q.SearchName) { - case "id": - id, err := strconv.Atoi(q.SearchKey) - if err == nil { - countArg.IsID = true - countArg.ID = int32(id) - - dataArg.IsID = true - dataArg.ID = int32(id) - } - case "name": - countArg.Name = q.SearchKey - dataArg.Name = q.SearchKey - } - } - count, err := db.Engine.CountSysDepartmentCondition(ctx, countArg) - if err != nil { - return nil, 0, err - } - - departs, err := db.Engine.ListSysDepartmentCondition(ctx, dataArg) - if err != nil { - return nil, 0, err - } - - return departs, count, nil -} - -func ListTreeSysDepartment(ctx context.Context) ([]*db.SysDepartmentDto, error) { - all, err := db.Engine.ListSysDepartment(ctx) - if err != nil { - return nil, err - } - - return ToDtoTreeSysDepartment(0, all), nil -} - -func DTreeSysDepartment(ctx context.Context, id int32) ([]*dto.DTreeDto, error) { - all, err := db.Engine.AllSysDepartment(ctx) - if err != nil { - return nil, err - } - - return toDtree(id, all), nil -} - -func RebuildSysDepartmentParentPath(ctx context.Context) error { - return db.Engine.SysDepartmentRebuildPath(ctx) -} - -func toDtree(parentId int32, data []*db.SysDepartment) []*dto.DTreeDto { - var res []*dto.DTreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.DTreeDto{} - item.ID = strconv.FormatInt(int64(v.ID), 10) - item.Title = v.Name - item.Last = !hasChildren(v.ID, data) - item.ParentId = strconv.FormatInt(int64(v.ParentID), 10) - item.Children = toDtree(v.ID, data) - res = append(res, &item) - } - } - - return res -} - -func hasChildren(parentId int32, data []*db.SysDepartment) bool { - if len(data) > 0 { - for _, v := range data { - if v.ParentID == parentId { - return true - } - } - } - - return false -} - -func ToTreeSysDepartment(ctx context.Context, id int, isRoot bool) ([]*dto.TreeDto, error) { - all, err := db.Engine.AllSysDepartment(ctx) - if err != nil { - return nil, err - } - - if isRoot { - root := getSysDepartmentRootParentId(int32(id), all) - if root == nil { - root = &dto.TreeDto{ - ID: 0, - Title: "根节点", - } - } - - root.Children = toDtoTreeSysDepartment(int32(id), all) - return []*dto.TreeDto{root}, nil - } - - return toDtoTreeSysDepartment(int32(id), all), nil -} - -func RefreshSysDepartment(ctx context.Context) error { - all, err := db.Engine.AllSysDepartment(ctx) - if err != nil { - return err - } - - b, err := json.Marshal(all) - if err != nil { - return err - } - - key := keys.GetManageKey(ctx, keys.AllDepartments) - err = redis.Set(ctx, key, b, time.Hour*6) - return err -} - -func getSysDepartmentRootParentId(parentId int32, data []*db.SysDepartment) *dto.TreeDto { - for _, v := range data { - if v.ID == parentId { - return &dto.TreeDto{ - ID: int(v.ID), - Title: v.Name, - } - } - } - return nil -} - -func ToDtoTreeSysDepartment(parentId int32, data []*db.SysDepartment) []*db.SysDepartmentDto { - var res []*db.SysDepartmentDto - for _, v := range data { - if v.ParentID == parentId { - item := db.SysDepartmentDto{} - item.ID = v.ID - item.Name = v.Name - item.ParentID = v.ParentID - item.ParentPath = v.ParentPath - item.Status = v.Status - item.CreatedAt = v.CreatedAt - item.UpdatedAt = v.UpdatedAt - item.Children = ToDtoTreeSysDepartment(v.ID, data) - res = append(res, &item) - } - } - - return res -} - -func toDtoTreeSysDepartment(parentId int32, data []*db.SysDepartment) []*dto.TreeDto { - var res []*dto.TreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.TreeDto{} - item.ID = int(v.ID) - item.Title = v.Name - item.Children = toDtoTreeSysDepartment(v.ID, data) - res = append(res, &item) - } - } - - return res -} diff --git a/internal/service/system/sys_menu.go b/internal/service/system/sys_menu.go deleted file mode 100644 index 0058541..0000000 --- a/internal/service/system/sys_menu.go +++ /dev/null @@ -1,568 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "strconv" - "strings" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/global/keys" - "management/internal/pkg/redis" -) - -func CreateSysMenu(ctx context.Context, arg *db.CreateSysMenuParams) (*db.SysMenu, error) { - return db.Engine.CreateSysMenu(ctx, arg) -} - -func UpdateSysMenu(ctx context.Context, arg *db.UpdateSysMenuParams) (*db.SysMenu, error) { - return db.Engine.UpdateSysMenu(ctx, arg) -} - -func GetSysMenu(ctx context.Context, id int32) (*db.SysMenu, error) { - return db.Engine.GetSysMenu(ctx, id) -} - -func GetSysMenuByUrl(ctx context.Context, url string) (*db.SysMenu, error) { - return db.Engine.GetSysMenuByUrl(ctx, url) -} - -func ListSysMenuByRoleID(ctx context.Context, roleID int32) ([]*db.SysMenu, error) { - return db.Engine.ListSysMenuByRoleID(ctx, roleID) -} - -func AllMenusCache(ctx context.Context) ([]*db.SysMenu, error) { - key := keys.GetManageKey(ctx, keys.AllMenus) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res []*db.SysMenu - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - all, err := db.Engine.AllSysMenu(ctx) - if err != nil { - return nil, err - } - - b, err = json.Marshal(all) - if err != nil { - return nil, err - } - - _ = redis.Set(ctx, key, b, time.Hour*6) - return all, nil -} - -func ListMenuTree(ctx context.Context) ([]*db.SysMenuDto, error) { - all, err := AllMenusCache(ctx) - if err != nil { - return nil, err - } - - return toTreeSysMenu(0, all), nil -} - -func ListMenuByRoles(ctx context.Context, roles []*db.SysRole) ([]*db.SysMenu, error) { - // 判断角色是否有vip - var vip bool - for _, item := range roles { - if item.Vip { - vip = true - break - } - } - - var err error - var userMenus []*db.SysMenu - if vip { - userMenus, err = AllMenusCache(ctx) - if err != nil { - return nil, err - } - } else { - for _, item := range roles { - ums, err := db.Engine.ListSysMenuByRoleID(ctx, item.ID) - if err != nil { - return nil, err - } - userMenus = append(userMenus, ums...) - } - } - - return userMenus, nil -} - -func IListMenuByUserID(ctx context.Context, userID int32) ([]*db.SysMenu, error) { - // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.AdminMenus, userID) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res []*db.SysMenu - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - return SetListMenuByUserID(ctx, userID) -} - -func SetListMenuByUserID(ctx context.Context, userID int32) ([]*db.SysMenu, error) { - // 判断当前用户是否有vip角色 - role, err := db.Engine.GetSysRoleByUserID(ctx, userID) - if err != nil { - return nil, err - } - - var e error - var menus []*db.SysMenu - if role.Vip { - // vip 用户 - menus, e = db.Engine.AllSysMenu(ctx) - if e != nil { - return nil, err - } - - } else { - // not vip - menus, e = db.Engine.AllSysMenu(ctx) // db.Engine.ListSysMenuByUserID(ctx, userID) - if e != nil { - return nil, err - } - } - - b, err := json.Marshal(menus) - if err != nil { - return nil, err - } - - key := keys.GetManageKey(ctx, keys.AdminMenus, userID) - _ = redis.Set(ctx, key, b, time.Hour*6) - return menus, nil -} - -func MapOwnerMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) { - // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res map[string]*dto.OwnerMenuDto - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - return SetOwnerMapMenuByRoleID(ctx, roleID) -} - -func ListOwnerMenuByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error) { - // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res []*dto.OwnerMenuDto - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - return SetOwnerListMenuByRoleID(ctx, roleID) -} - -func ownerMenusByRoleID(ctx context.Context, roleID int32) ([]*db.SysMenu, error) { - // 判断当前用户是否有vip角色 - role, err := db.Engine.GetSysRole(ctx, roleID) - if err != nil { - return nil, err - } - - var e error - var menus []*db.SysMenu - if role.Vip { - // vip 用户 - menus, e = db.Engine.AllSysMenu(ctx) - if e != nil { - return nil, err - } - - } else { - // not vip - menus, e = db.Engine.ListSysMenuByRoleID(ctx, roleID) - if e != nil { - return nil, err - } - } - - return menus, nil -} - -func SetOwnerListMenuByRoleID(ctx context.Context, roleID int32) ([]*dto.OwnerMenuDto, error) { - menus, err := ownerMenusByRoleID(ctx, roleID) - if err != nil { - return nil, err - } - - var res []*dto.OwnerMenuDto - for _, menu := range menus { - res = append(res, &dto.OwnerMenuDto{ - ID: menu.ID, - DisplayName: menu.DisplayName, - Url: menu.Url, - ParentID: menu.ParentID, - Avatar: menu.Avatar, - Style: menu.Style, - IsList: menu.IsList, - }) - } - - b, err := json.Marshal(res) - if err != nil { - return nil, err - } - - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) - _ = redis.Set(ctx, key, b, time.Hour*6) - return res, nil -} - -func SetOwnerMapMenuByRoleID(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) { - result := make(map[string]*dto.OwnerMenuDto) - menus, err := ownerMenusByRoleID(ctx, roleID) - if err != nil { - return result, err - } - - for _, menu := range menus { - result[menu.Url] = &dto.OwnerMenuDto{ - ID: menu.ID, - DisplayName: menu.DisplayName, - Url: menu.Url, - ParentID: menu.ParentID, - Avatar: menu.Avatar, - Style: menu.Style, - IsList: menu.IsList, - } - } - - b, err := json.Marshal(result) - if err != nil { - return nil, err - } - - key := keys.GetManageKey(ctx, keys.OwnerMenus, roleID) - _ = redis.Set(ctx, key, b, time.Hour*6) - return result, nil -} - -// RecursiveSysMenus 递归查询菜单 (pear layui 展示菜单的格式) -func RecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error) { - // 判断redis是否存储 - key := keys.GetManageKey(ctx, keys.RecursiveMenus, roleID) - b, err := redis.GetBytes(ctx, key) - if err == nil { - var res []*dto.MenuUIDto - if err := json.Unmarshal(b, &res); err == nil { - return res, nil - } - } - - return SetRecursiveSysMenus(ctx, roleID) -} - -func SetRecursiveSysMenus(ctx context.Context, roleID int32) ([]*dto.MenuUIDto, error) { - // 判断当前用户是否有vip角色 - role, err := db.Engine.GetSysRole(ctx, roleID) - if err != nil { - return nil, err - } - - var menus []*db.SysMenu - if role.Vip { - // vip 用户 - all, err := db.Engine.RecursiveSysMenus(ctx) - if err != nil { - return nil, err - } - menus = convertToMenuUIDto(all) - - } else { - // not vip - all, err := db.Engine.RecursiveSysMenusByRoleID(ctx, roleID) - if err != nil { - return nil, err - } - menus = convertToMenuUIDto2(all) - } - menuList := uniqueSysMenus(menus) - if len(menuList) == 0 { - return nil, nil - } - - tree := convertToUITree(menuList, 0) - b, err := json.Marshal(tree) - if err != nil { - return nil, err - } - - key := keys.GetManageKey(ctx, keys.RecursiveMenus, roleID) - _ = redis.Set(ctx, key, b, time.Hour*6) - return tree, nil -} - -func convertToMenuUIDto(data []*db.RecursiveSysMenusRow) []*db.SysMenu { - var res []*db.SysMenu - - for _, item := range data { - temp := &db.SysMenu{ - ID: item.ID, - Name: item.Name, - DisplayName: item.DisplayName, - Url: item.Url, - Type: item.Type, - ParentID: item.ParentID, - ParentPath: item.ParentPath, - Avatar: item.Avatar, - Style: item.Style, - Visible: item.Visible, - IsList: item.IsList, - Status: item.Status, - Sort: item.Sort, - CreatedAt: item.CreatedAt, - UpdatedAt: item.UpdatedAt, - } - res = append(res, temp) - } - - return res -} - -func convertToMenuUIDto2(data []*db.RecursiveSysMenusByRoleIDRow) []*db.SysMenu { - var res []*db.SysMenu - - for _, item := range data { - temp := &db.SysMenu{ - ID: item.ID, - Name: item.Name, - DisplayName: item.DisplayName, - Url: item.Url, - Type: item.Type, - ParentID: item.ParentID, - ParentPath: item.ParentPath, - Avatar: item.Avatar, - Style: item.Style, - Visible: item.Visible, - IsList: item.IsList, - Status: item.Status, - Sort: item.Sort, - CreatedAt: item.CreatedAt, - UpdatedAt: item.UpdatedAt, - } - res = append(res, temp) - } - - return res -} - -func convertToUITree(data []*db.SysMenu, parentID int32) []*dto.MenuUIDto { - var root []*dto.MenuUIDto - for _, item := range data { - if item.ParentID == parentID { - if item.IsList { - temp := &dto.MenuUIDto{ - ID: strings.ToLower(item.Url), - Title: item.DisplayName, - Icon: item.Avatar, - Type: 1, - OpenType: "_iframe", - // OpenType: "_component", - Href: item.Url, - } - root = append(root, temp) - } else { - temp := &dto.MenuUIDto{ - ID: strconv.Itoa(int(item.ID)), - Title: item.DisplayName, - Icon: item.Avatar, - Type: 0, - } - temp.Children = convertToUITree(data, item.ID) - root = append(root, temp) - } - } - } - return root -} - -func uniqueSysMenus(sm []*db.SysMenu) []*db.SysMenu { - res := make([]*db.SysMenu, 0) // 返回的新切片 - m1 := make(map[int32]byte) // 用来去重的临时map - for _, v := range sm { - if _, ok := m1[v.ID]; !ok { - m1[v.ID] = 1 - res = append(res, v) - } - } - return res -} - -func ToTreeMenu(ctx context.Context, id int, hasRoot bool) ([]*dto.TreeDto, error) { - all, err := AllMenusCache(ctx) - if err != nil { - return nil, err - } - - if hasRoot { - root := getSysMenuRootParentId(int32(id), all) - if root == nil { - root = &dto.TreeDto{ - ID: 0, - Title: "根节点", - } - } - - root.Children = toDtoTreeSysMenu(int32(id), all) - return []*dto.TreeDto{root}, nil - } - - return toDtoTreeSysMenu(int32(id), all), nil -} - -func RefreshMenus(ctx context.Context) error { - key := keys.GetManageKey(ctx, keys.AllMenus) - all, err := db.Engine.AllSysMenu(ctx) - if err != nil { - return err - } - - b, err := json.Marshal(all) - if err != nil { - return err - } - - err = redis.Set(ctx, key, b, time.Hour*6) - return err -} - -func getSysMenuRootParentId(parentId int32, data []*db.SysMenu) *dto.TreeDto { - for _, v := range data { - if v.ID == parentId { - return &dto.TreeDto{ - ID: int(v.ID), - Title: v.Name, - } - } - } - return nil -} - -func toTreeSysMenu(parentId int32, data []*db.SysMenu) []*db.SysMenuDto { - var res []*db.SysMenuDto - for _, v := range data { - if v.ParentID == parentId { - item := db.SysMenuDto{} - item.ID = v.ID - item.Name = v.Name - item.DisplayName = v.DisplayName - item.Url = v.Url - item.Type = v.Type - item.ParentID = v.ParentID - item.ParentPath = v.ParentPath - item.Avatar = v.Avatar - item.Style = v.Style - item.Visible = v.Visible - item.IsList = v.IsList - item.Status = v.Status - item.Sort = v.Sort - item.CreatedAt = v.CreatedAt - item.UpdatedAt = v.UpdatedAt - item.Children = toTreeSysMenu(v.ID, data) - res = append(res, &item) - } - } - - return res -} - -func toDtoTreeSysMenu(parentId int32, data []*db.SysMenu) []*dto.TreeDto { - var res []*dto.TreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.TreeDto{} - item.ID = int(v.ID) - item.Title = v.Name - item.Children = toDtoTreeSysMenu(v.ID, data) - res = append(res, &item) - } - } - - return res -} - -func SetMenuViewData(ctx context.Context, roleID int32) ([]*dto.SetMenuDto, error) { - // 获取该用户已经有的权限 - hs, err := db.Engine.ListSysMenuIDByRoleID(ctx, roleID) - if err != nil { - return nil, err - } - - all, err := AllMenusCache(ctx) - if err != nil { - return nil, err - } - - return toSetMenuTree(all, hs, 0), nil -} - -func toSetMenuTree(data []*db.SysMenu, ids []int32, parentID int32) []*dto.SetMenuDto { - var res []*dto.SetMenuDto - for _, v := range data { - if v.ParentID == parentID { - isSelect := hasValueInArray(ids, v.ID) - if v.IsList { - item := dto.SetMenuDto{ - ID: v.ID, - Name: v.DisplayName, - Link: v.Type, - IsList: v.IsList, - IsSelect: isSelect, - } - item.Items = []*dto.SetMenuDto{ - { - ID: v.ID, - Name: "列表", - Link: "btn", - IsList: false, - IsSelect: isSelect, - Items: toSetMenuTree(data, ids, v.ID), - }, - } - item.Items = append(item.Items, toSetMenuTree(data, ids, v.ID)...) - res = append(res, &item) - } else { - item := dto.SetMenuDto{ - ID: v.ID, - Name: v.DisplayName, - Link: v.Type, - IsList: v.IsList, - IsSelect: isSelect, - Items: toSetMenuTree(data, ids, v.ID), - } - res = append(res, &item) - } - } - } - - return res -} - -func hasValueInArray(data []int32, id int32) bool { - for _, v := range data { - if id == v { - return true - } - } - return false -} diff --git a/internal/service/system/sys_role.go b/internal/service/system/sys_role.go deleted file mode 100644 index 4f99ea1..0000000 --- a/internal/service/system/sys_role.go +++ /dev/null @@ -1,164 +0,0 @@ -package system - -import ( - "context" - "encoding/json" - "strconv" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" - "management/internal/global/keys" - "management/internal/pkg/redis" -) - -func CreateSysRole(ctx context.Context, arg *db.CreateSysRoleParams) (*db.SysRole, error) { - return db.Engine.CreateSysRole(ctx, arg) -} - -func UpdateSysRole(ctx context.Context, arg *db.UpdateSysRoleParams) (*db.SysRole, error) { - return db.Engine.UpdateSysRole(ctx, arg) -} - -func GetSysRole(ctx context.Context, id int32) (*db.SysRole, error) { - return db.Engine.GetSysRole(ctx, id) -} - -func ListSysRoleCondition(ctx context.Context, q dto.SearchDto) ([]*db.SysRole, int64, error) { - countArg := &db.CountSysRoleConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int32(q.SearchStatus), - IsID: q.SearchID != 0, - ID: int32(q.SearchID), - IsParentID: q.SearchParentID != 0, - ParentID: int32(q.SearchParentID), - DisplayName: q.SearchName, - } - - dataArg := &db.ListSysRoleConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int32(q.SearchStatus), - IsID: q.SearchID != 0, - ID: int32(q.SearchID), - IsParentID: q.SearchParentID != 0, - ParentID: int32(q.SearchParentID), - DisplayName: q.SearchName, - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - } - count, err := db.Engine.CountSysRoleCondition(ctx, countArg) - if err != nil { - return nil, 0, err - } - - roles, err := db.Engine.ListSysRoleCondition(ctx, dataArg) - if err != nil { - return nil, 0, err - } - - return roles, count, nil -} - -func XmSelectSysRole(ctx context.Context, id int32) ([]*dto.XmSelectTreeDto, error) { - all, err := db.Engine.AllSysRole(ctx) - if err != nil { - return nil, err - } - - return toXmSelectTree(id, all), nil -} - -func toXmSelectTree(parentId int32, data []*db.SysRole) []*dto.XmSelectTreeDto { - var res []*dto.XmSelectTreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.XmSelectTreeDto{ - Name: v.Name, - Value: strconv.FormatInt(int64(v.ID), 10), - Children: toXmSelectTree(v.ID, data), - } - res = append(res, &item) - } - } - - return res -} - -func SetMenu(ctx context.Context, roleID int32, menus []*db.SysMenu) error { - return db.Engine.ExecTx(ctx, func(q *db.Queries) error { - err := db.Engine.DeleteRoleMneuByRoleID(ctx, roleID) - if err != nil { - return err - } - - for _, m := range menus { - err := db.Engine.CreateRoleMenu(ctx, &db.CreateRoleMenuParams{ - RoleID: roleID, - MenuID: m.ID, - }) - if err != nil { - return err - } - } - - return nil - }) -} - -func DTreeSysRole(ctx context.Context, id int32) ([]*dto.DTreeDto, error) { - all, err := db.Engine.AllSysRole(ctx) - if err != nil { - return nil, err - } - - return toDtreeSysRole(id, all), nil -} - -func RefreshSysRole(ctx context.Context) error { - all, err := db.Engine.AllSysRole(ctx) - if err != nil { - return err - } - - b, err := json.Marshal(all) - if err != nil { - return err - } - - key := keys.GetManageKey(ctx, keys.AllRoles) - err = redis.Set(ctx, key, b, time.Hour*6) - return err -} - -func RebuildSysRoleParentPath(ctx context.Context) error { - return db.Engine.SysRoleRebuildPath(ctx) -} - -func toDtreeSysRole(parentId int32, data []*db.SysRole) []*dto.DTreeDto { - var res []*dto.DTreeDto - for _, v := range data { - if v.ParentID == parentId { - item := dto.DTreeDto{} - item.ID = strconv.FormatInt(int64(v.ID), 10) - item.Title = v.DisplayName - item.Last = !hasSysRoleChildren(v.ID, data) - item.ParentId = strconv.FormatInt(int64(v.ParentID), 10) - item.Children = toDtreeSysRole(v.ID, data) - res = append(res, &item) - } - } - - return res -} - -func hasSysRoleChildren(parentId int32, data []*db.SysRole) bool { - if len(data) > 0 { - for _, v := range data { - if v.ParentID == parentId { - return true - } - } - } - - return false -} diff --git a/internal/service/system/sys_user.go b/internal/service/system/sys_user.go deleted file mode 100644 index 7761ce0..0000000 --- a/internal/service/system/sys_user.go +++ /dev/null @@ -1,48 +0,0 @@ -package system - -import ( - "context" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" -) - -func CreateSysUser(ctx context.Context, arg *db.CreateSysUserParams) (*db.SysUser, error) { - return db.Engine.CreateSysUser(ctx, arg) -} - -func UpdateSysUser(ctx context.Context, arg *db.UpdateSysUserParams) (*db.SysUser, error) { - return db.Engine.UpdateSysUser(ctx, arg) -} - -func GetSysUser(ctx context.Context, id int32) (*db.SysUser, error) { - return db.Engine.GetSysUser(ctx, id) -} - -func GetSysUserByEmail(ctx context.Context, email string) (*db.SysUser, error) { - return db.Engine.GetSysUserByEmail(ctx, email) -} - -func ListSysUserCondition(ctx context.Context, q dto.SearchDto) ([]*db.ListSysUserConditionRow, int64, error) { - count, err := db.Engine.CountSysUserCondition(ctx, &db.CountSysUserConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int32(q.SearchStatus), - Username: q.SearchKey, - }) - if err != nil { - return nil, 0, err - } - - users, err := db.Engine.ListSysUserCondition(ctx, &db.ListSysUserConditionParams{ - IsStatus: q.SearchStatus != 9999, - Status: int32(q.SearchStatus), - Username: q.SearchKey, - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - }) - if err != nil { - return nil, 0, err - } - - return users, count, nil -} diff --git a/internal/service/system/sys_user_login_log.go b/internal/service/system/sys_user_login_log.go deleted file mode 100644 index 70e8250..0000000 --- a/internal/service/system/sys_user_login_log.go +++ /dev/null @@ -1,59 +0,0 @@ -package system - -import ( - "context" - "strings" - "time" - - "management/internal/db/model/dto" - db "management/internal/db/sqlc" -) - -func CreateSysUserLoginLog(ctx context.Context, arg *db.CreateSysUserLoginLogParams) error { - return db.Engine.CreateSysUserLoginLog(ctx, arg) -} - -func ListSysUserLoginLog(ctx context.Context, q dto.SearchDto) ([]*db.SysUserLoginLog, int64, error) { - start, err := time.ParseInLocation(time.DateTime, q.SearchTimeBegin, time.Local) - if err != nil { - return nil, 0, err - } - end, err := time.ParseInLocation(time.DateTime, q.SearchTimeEnd, time.Local) - if err != nil { - return nil, 0, err - } - - countArg := &db.CountSysUserLoginLogConditionParams{ - StartAt: start, - EndAt: end, - } - - dataArg := &db.ListSysUserLoginLogConditionParams{ - StartAt: start, - EndAt: end, - Skip: (int32(q.Page) - 1) * int32(q.Rows), - Size: int32(q.Rows), - } - - if len(q.SearchKey) > 0 { - switch strings.ToLower(q.SearchName) { - case "email": - countArg.Email = q.SearchKey - dataArg.Email = q.SearchKey - case "username": - countArg.Username = q.SearchKey - dataArg.Username = q.SearchKey - } - } - count, err := db.Engine.CountSysUserLoginLogCondition(ctx, countArg) - if err != nil { - return nil, 0, err - } - - logs, err := db.Engine.ListSysUserLoginLogCondition(ctx, dataArg) - if err != nil { - return nil, 0, err - } - - return logs, count, nil -} diff --git a/internal/service/tencentoss/tencentoss.go b/internal/service/tencentoss/tencentoss.go deleted file mode 100644 index fbd3fad..0000000 --- a/internal/service/tencentoss/tencentoss.go +++ /dev/null @@ -1,162 +0,0 @@ -package tencentoss - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "io/fs" - "mime/multipart" - "net/http" - "net/url" - "os" - "path" - - "management/internal/config" - fileutil "management/internal/pkg/file" - - "github.com/h2non/filetype" - "github.com/tencentyun/cos-go-sdk-v5" -) - -var engine *cos.Client - -func Init() error { - u, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", config.File.TencentUpload.Bucket, config.File.TencentUpload.Region)) - if err != nil { - return err - } - - b := &cos.BaseURL{BucketURL: u} - engine = cos.NewClient(b, &http.Client{ - Transport: &cos.AuthorizationTransport{ - // 通过环境变量获取密钥 - // 环境变量 SECRETID 表示用户的 SecretId,登录访问管理控制台查看密钥,https://console.cloud.tencent.com/cam/capi - SecretID: config.File.TencentUpload.AccessKeyID, // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140 - // 环境变量 SECRETKEY 表示用户的 SecretKey,登录访问管理控制台查看密钥,https://console.cloud.tencent.com/cam/capi - SecretKey: config.File.TencentUpload.AccessKeySecret, // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140 - }, - }) - - return nil -} - -func UploadFile(ctx context.Context, file *multipart.FileHeader, t fileutil.FileType) (string, error) { - if file.Size > config.File.TencentUpload.AllowFileMaxSize { - return "", errors.New("failed to receive file too large") - } - - fileOpen, err := file.Open() - if err != nil { - return "", errors.New("failed to file open") - } - defer func(fileOpen multipart.File) { - _ = fileOpen.Close() - }(fileOpen) - - fileBytes, err := io.ReadAll(fileOpen) - if err != nil { - return "", errors.New("failed to read file") - } - - if t == fileutil.IMG { - // 判断是不是图片 - if !filetype.IsImage(fileBytes) { - return "", fileutil.ErrUnsupported - } - } - - kind, err := filetype.Match(fileBytes) - if err != nil || kind == filetype.Unknown { - return "", errors.New("failed to get file type") - } - - filename := fileutil.GenFilename(kind.Extension) - imgPath := path.Join(fileutil.GetPath(), filename) - _, err = engine.Object.Put(ctx, imgPath, bytes.NewReader(fileBytes), nil) - if err != nil { - return "", err - } - - return imgPath, nil -} - -func UploadFileOther(ctx context.Context, p string, t fileutil.FileType) (string, error) { - file, err := os.Open(p) - if err != nil { - return "", err - } - defer func(file *os.File) { - _ = file.Close() - }(file) - - fileBytes, err := io.ReadAll(file) - if err != nil { - return "", errors.New("failed to read file") - } - - if t == fileutil.IMG { - // 判断是不是图片 - if !filetype.IsImage(fileBytes) { - return "", fileutil.ErrUnsupported - } - } - - kind, err := filetype.Match(fileBytes) - if err != nil || kind == filetype.Unknown { - return "", errors.New("failed to get file type") - } - - filename := fileutil.GenFilename(kind.Extension) - imgPath := path.Join(fileutil.GetPath(), filename) - _, err = engine.Object.Put(ctx, imgPath, bytes.NewReader(fileBytes), nil) - if err != nil { - return "", err - } - - return imgPath, nil -} - -func UploadFileByFS(ctx context.Context, file fs.File, t fileutil.FileType) (string, error) { - fileBytes, err := io.ReadAll(file) - if err != nil { - return "", errors.New("failed to read file") - } - - if t == fileutil.IMG { - // 判断是不是图片 - if !filetype.IsImage(fileBytes) { - return "", fileutil.ErrUnsupported - } - } - - kind, err := filetype.Match(fileBytes) - if err != nil || kind == filetype.Unknown { - return "", errors.New("failed to get file type") - } - - filename := fileutil.GenFilename(kind.Extension) - imgPath := path.Join(fileutil.GetPath(), filename) - _, err = engine.Object.Put(ctx, imgPath, bytes.NewReader(fileBytes), nil) - if err != nil { - return "", err - } - - return imgPath, nil -} - -func DownloadFile(ctx context.Context, name string) ([]byte, error) { - resp, err := engine.Object.Get(ctx, name, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - bs, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return bs, nil -} diff --git a/internal/tpl/html.go b/internal/tpl/html.go deleted file mode 100644 index 7a5dcf9..0000000 --- a/internal/tpl/html.go +++ /dev/null @@ -1,52 +0,0 @@ -package tpl - -import ( - "bytes" - "net/http" - "path/filepath" - "strings" - - "management/internal/db/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 HTML(w http.ResponseWriter, r *http.Request, tpl string, data map[string]any) { - rndr.HTML(w, r, tpl, data) -} - -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/tpl/html_btn.go b/internal/tpl/html_btn.go deleted file mode 100644 index 9ee3b97..0000000 --- a/internal/tpl/html_btn.go +++ /dev/null @@ -1,91 +0,0 @@ -package tpl - -import ( - "html/template" - "path/filepath" - "strings" - - "management/internal/db/model/dto" -) - -func (r *render) btnFuncs() 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("") - } - - var res string - for _, action := range actionNames { - for _, btn := range btns { - btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui") - base := filepath.Base(btn.Url) - if base == action { - res += `` - } - } - } - - return template.HTML(res) - } - - res["genLink"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML { - if len(btns) == 0 { - return template.HTML("") - } - - var res string - for _, action := range actionNames { - for _, btn := range btns { - btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui") - base := filepath.Base(btn.Url) - if base == action { - res += `` - } - } - } - - return template.HTML(res) - } - - res["previewPicture"] = func(name string) template.HTML { - var res string - res += `` - - return template.HTML(res) - } - - res["submitBtn"] = func(btns []*dto.OwnerMenuDto, actionNames ...string) template.HTML { - if len(btns) == 0 { - return template.HTML("") - } - - var res string - for _, action := range actionNames { - for _, btn := range btns { - btn.Style = strings.ReplaceAll(btn.Style, "pear", "layui") - base := filepath.Base(btn.Url) - if base == action { - res += `` - } - } - } - - return template.HTML(res) - } - - return res -} diff --git a/internal/tpl/html_method.go b/internal/tpl/html_method.go deleted file mode 100644 index 36accad..0000000 --- a/internal/tpl/html_method.go +++ /dev/null @@ -1,57 +0,0 @@ -package tpl - -import ( - "html/template" - "strings" - "time" -) - -func (r *render) Methods() map[string]any { - res := make(map[string]any, 1) - - res["dateFormat"] = func(dt time.Time) template.HTML { - return template.HTML(dt.Format(time.DateTime)) - } - - res["today"] = func() template.HTML { - return template.HTML(time.Now().Format("2006-01-02")) - } - - res["threeMonth"] = func() template.HTML { - return template.HTML(time.Now().AddDate(0, 3, 0).Format("2006-01-02")) - } - - res["yearBegin"] = func() template.HTML { - dt := time.Now() - t := dt.AddDate(0, -int(dt.Month())+1, -dt.Day()+1) - return template.HTML(t.Format("2006-01-02") + " 00:00:00") - } - - res["monthBegin"] = func() template.HTML { - dt := time.Now() - t := dt.AddDate(0, 0, -dt.Day()+1) - return template.HTML(t.Format("2006-01-02") + " 00:00:00") - } - - res["monthEnd"] = func() template.HTML { - dt := time.Now() - t := dt.AddDate(0, 0, -dt.Day()+1).AddDate(0, 1, -1) - return template.HTML(t.Format("2006-01-02") + " 23:59:59") - } - - res["trimSpace"] = func(s string) template.HTML { - return template.HTML(strings.TrimSpace(s)) - } - - res["expandTags"] = func(s []string) template.HTML { - if len(s) == 0 { - return template.HTML("") - } - if len(s) == 1 && s[0] == "all" { - return template.HTML("") - } - return template.HTML(strings.Join(s, ",")) - } - - return res -} diff --git a/internal/tpl/json.go b/internal/tpl/json.go deleted file mode 100644 index 1570cff..0000000 --- a/internal/tpl/json.go +++ /dev/null @@ -1,73 +0,0 @@ -package tpl - -import ( - "encoding/json" - "net/http" -) - -type Response struct { - Success bool `json:"success"` - Message string `json:"msg"` - Data any `json:"data"` -} - -type ResponseDtree struct { - Status ResponseDtreeStatus `json:"status"` - Data any `json:"data"` -} - -type ResponseDtreeStatus struct { - Code int `json:"code"` - Message string `json:"message"` -} - -type ResponseList struct { - Code int `json:"code"` - Message string `json:"msg"` - Count int64 `json:"count"` - Data any `json:"data"` -} - -func JSON(w http.ResponseWriter, data any) { - rndr.JSON(w, data) -} - -func JSONF(w http.ResponseWriter, success bool, message string) { - rndr.JSONF(w, success, message) -} - -func JSONOK(w http.ResponseWriter, message string) { - rndr.JSONOK(w, message) -} - -func JSONERR(w http.ResponseWriter, message string) { - rndr.JSONERR(w, message) -} - -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) { - r.JSON(w, Response{Success: true, Message: message}) -} - -func (r *render) JSONERR(w http.ResponseWriter, message string) { - r.JSON(w, Response{Success: false, Message: message}) -} - -func (r *render) JSON(w http.ResponseWriter, data any) { - v, err := json.Marshal(data) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - _, err = w.Write(v) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} diff --git a/internal/tpl/render.go b/internal/tpl/render.go deleted file mode 100644 index c7fbb6a..0000000 --- a/internal/tpl/render.go +++ /dev/null @@ -1,44 +0,0 @@ -package tpl - -import ( - "html/template" - "net/http" - - "management/internal/pkg/session" -) - -var rndr Renderer - -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.ISession - config *TemplateConfig - templates map[string]*template.Template -} - -func New(session session.ISession) (Renderer, error) { - render := &render{ - session: session, - 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/tpl/util.go b/internal/tpl/util.go deleted file mode 100644 index 068c88e..0000000 --- a/internal/tpl/util.go +++ /dev/null @@ -1,182 +0,0 @@ -package tpl - -import ( - "context" - "encoding/json" - "fmt" - "html/template" - "io/fs" - "net/http" - "os" - "path/filepath" - "slices" - "strings" - - "management/internal/db/model/dto" - "management/internal/global/auth" - systemservice "management/internal/service/system" - 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, auth.StoreName) - data["IsAuthenticated"] = isAuth - if isAuth { - var authUser dto.AuthorizeUser - u := r.session.GetBytes(ctx, auth.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 - - // 获取当前path的菜单 - menu, err := systemservice.GetSysMenuByUrl(ctx, path) - if err != nil { - return res - } - - // 获取权限 - menus, err := systemservice.ListOwnerMenuByRoleID(ctx, roleID) - if err != nil { - 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/management b/management index 028aeb2..de06472 100755 Binary files a/management and b/management differ diff --git a/web/templates/manage/budget/edit.tmpl b/web/templates/manage/budget/edit.tmpl index a9af92a..669f5e9 100644 --- a/web/templates/manage/budget/edit.tmpl +++ b/web/templates/manage/budget/edit.tmpl @@ -137,7 +137,7 @@
{{ submitBtn .AuthorizeMenus "save"}} -
@@ -160,14 +160,12 @@ {{define "js"}} + +
@@ -118,6 +175,7 @@ table.on('toolbar(tablelist)', function (obj) { switch (obj.event) { case 'add': add(); break; + case 'search': search(); break; case 'refresh': refresh(); break; } }); @@ -134,6 +192,37 @@ }); } + function search() { + layer.open({ + type: 1, + offset: '20px', + title: '搜索', + content: $('#search-layer'), // 捕获的元素 + shade: 0.1, + shadeClose: false, + scrollbar: false, + resize: false, + move: false, + skin: 'search-layer-open', + area: ['50%', '350px'], + btn: ['搜索', '重置'], + btn1: function (index, layero) { + search_btn(); + layer.close(index); + }, + btn2: function (index, layero) { + $('#timeBegin').val(formatDate(getCurrentYearStart(), 'YYYY-MM-DD HH:mm:ss')); + $('#timeEnd').val(formatDate(getCurrentMonthEnd(), 'YYYY-MM-DD HH:mm:ss')); + $('#title').val(''); + $('#category').val(''); + $('#source').val(''); + $('#status').val(9999); + form.render('select'); + return false; + } + }); + } + function refresh() { table.reload('tablelist'); } @@ -171,16 +260,14 @@ } // 搜索 - function search() { - form.on('submit(user-query)', function (data) { - table.reload('tablelist', { - where: getQueryParams(), - page: { - curr: 1 - } - }) - return false; - }); + function search_btn() { + table.reload('tablelist', { + where: getQueryParams(), + page: { + curr: 1 + } + }) + return false; } }); diff --git a/web/templates/manage/expense/edit.tmpl b/web/templates/manage/expense/edit.tmpl index fea1ff9..4786b18 100644 --- a/web/templates/manage/expense/edit.tmpl +++ b/web/templates/manage/expense/edit.tmpl @@ -122,11 +122,9 @@ {{define "js"}} -
-
-
- + + +
+
+
+ {{define "js"}} -
-
-
- + + +
+
+
+ {{define "js"}}