├── .air.conf ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd └── gin-amis-admin │ └── main.go ├── configs ├── config.toml ├── menu.yaml ├── model.conf ├── page_manager.yaml └── tpl_mall.yaml ├── docs └── data_model.md ├── gin-amis-admin ├── go.mod ├── go.sum ├── internal └── app │ ├── api │ ├── a_app.go │ ├── a_demo.go │ ├── a_g_platform.go │ ├── a_g_tpl_mall.go │ ├── a_login.go │ ├── a_menu.go │ ├── a_page_manager.go │ ├── a_page_version_history.go │ ├── a_role.go │ ├── a_setting.go │ ├── a_user.go │ ├── api.go │ └── mock │ │ ├── mock.go │ │ ├── mock_demo.go │ │ ├── mock_login.go │ │ ├── mock_menu.go │ │ ├── mock_role.go │ │ └── mock_user.go │ ├── app.go │ ├── bll │ ├── b_app.go │ ├── b_demo.go │ ├── b_g_platform.go │ ├── b_g_tpl_mall.go │ ├── b_login.go │ ├── b_menu.go │ ├── b_page_manager.go │ ├── b_page_version_history.go │ ├── b_role.go │ ├── b_setting.go │ ├── b_user.go │ └── impl │ │ └── bll │ │ ├── b_app.go │ │ ├── b_casbin.go │ │ ├── b_common.go │ │ ├── b_demo.go │ │ ├── b_g_platform.go │ │ ├── b_g_tpl_mall.go │ │ ├── b_login.go │ │ ├── b_menu.go │ │ ├── b_page_manager.go │ │ ├── b_page_version_history.go │ │ ├── b_role.go │ │ ├── b_setting.go │ │ ├── b_user.go │ │ └── bll.go │ ├── config │ └── config.go │ ├── ginplus │ └── ginplus.go │ ├── icontext │ └── context.go │ ├── injector │ ├── auth.go │ ├── casbin.go │ ├── gorm.go │ ├── injector.go │ ├── mongo.go │ ├── web.go │ ├── wire.go │ └── wire_gen.go │ ├── iutil │ ├── jsonpathx.go │ └── util.go │ ├── logger.go │ ├── middleware │ ├── middleware.go │ ├── mw_app_id.go │ ├── mw_auth.go │ ├── mw_casbin.go │ ├── mw_copy_body.go │ ├── mw_cors.go │ ├── mw_logger.go │ ├── mw_rate_limiter.go │ ├── mw_recover.go │ ├── mw_trace.go │ └── mw_www.go │ ├── model │ ├── impl │ │ ├── gorm │ │ │ ├── entity │ │ │ │ ├── e_demo.go │ │ │ │ ├── e_g_platform.go │ │ │ │ ├── e_g_tpl_mall.go │ │ │ │ ├── e_menu.go │ │ │ │ ├── e_menu_action.go │ │ │ │ ├── e_menu_action_resource.go │ │ │ │ ├── e_page_manager.go │ │ │ │ ├── e_page_version_history.go │ │ │ │ ├── e_role.go │ │ │ │ ├── e_role_menu.go │ │ │ │ ├── e_setting.go │ │ │ │ ├── e_user.go │ │ │ │ ├── e_user_role.go │ │ │ │ └── entity.go │ │ │ ├── gorm.go │ │ │ └── model │ │ │ │ ├── m_app.go │ │ │ │ ├── m_common.go │ │ │ │ ├── m_demo.go │ │ │ │ ├── m_g_platform.go │ │ │ │ ├── m_g_tpl_mall.go │ │ │ │ ├── m_menu.go │ │ │ │ ├── m_menu_action.go │ │ │ │ ├── m_menu_action_resource.go │ │ │ │ ├── m_page_manager.go │ │ │ │ ├── m_page_version_history.go │ │ │ │ ├── m_role.go │ │ │ │ ├── m_role_menu.go │ │ │ │ ├── m_setting.go │ │ │ │ ├── m_trans.go │ │ │ │ ├── m_user.go │ │ │ │ ├── m_user_role.go │ │ │ │ └── model.go │ │ └── mongo │ │ │ ├── entity │ │ │ ├── e_demo.go │ │ │ ├── e_menu.go │ │ │ ├── e_menu_action.go │ │ │ ├── e_menu_action_resource.go │ │ │ ├── e_role.go │ │ │ ├── e_role_menu.go │ │ │ ├── e_user.go │ │ │ ├── e_user_role.go │ │ │ └── entity.go │ │ │ ├── model │ │ │ ├── m_common.go │ │ │ ├── m_demo.go │ │ │ ├── m_menu.go │ │ │ ├── m_menu_action.go │ │ │ ├── m_menu_action_resource.go │ │ │ ├── m_role.go │ │ │ ├── m_role_menu.go │ │ │ ├── m_trans.go │ │ │ ├── m_user.go │ │ │ ├── m_user_role.go │ │ │ └── model.go │ │ │ └── mongo.go │ ├── m_app.go │ ├── m_demo.go │ ├── m_g_platform.go │ ├── m_g_tpl_mall.go │ ├── m_menu.go │ ├── m_menu_action.go │ ├── m_menu_action_resource.go │ ├── m_page_manager.go │ ├── m_page_version_history.go │ ├── m_role.go │ ├── m_role_menu.go │ ├── m_setting.go │ ├── m_trans.go │ ├── m_user.go │ └── m_user_role.go │ ├── module │ └── adapter │ │ └── casbin.go │ ├── router │ ├── r_api.go │ └── router.go │ ├── schema │ ├── s_app.go │ ├── s_demo.go │ ├── s_g_platform.go │ ├── s_g_tpl_mall.go │ ├── s_login.go │ ├── s_menu.go │ ├── s_page_manager.go │ ├── s_page_version_history.go │ ├── s_role.go │ ├── s_setting.go │ ├── s_user.go │ └── schema.go │ ├── swagger.go │ ├── swagger │ ├── docs.go │ ├── swagger.json │ └── swagger.yaml │ └── test │ ├── doc.go │ ├── t_demo_test.go │ ├── t_init_test.go │ ├── t_jsonpathx_test.go │ ├── t_menu_test.go │ ├── t_role_test.go │ └── t_user_test.go ├── pkg ├── auth │ ├── auth.go │ └── jwtauth │ │ ├── auth.go │ │ ├── auth_test.go │ │ ├── store.go │ │ ├── store │ │ ├── buntdb │ │ │ ├── buntdb.go │ │ │ └── buntdb_test.go │ │ └── redis │ │ │ ├── redis.go │ │ │ └── redis_test.go │ │ └── token.go ├── errors │ ├── errors.go │ └── response.go ├── logger │ ├── hook │ │ ├── gorm │ │ │ └── gorm.go │ │ ├── hook.go │ │ └── mongo │ │ │ └── mongo.go │ └── logger.go ├── trace │ └── trace.go ├── unique │ ├── object_id.go │ ├── snowflake.go │ └── uuid.go └── util │ ├── hash.go │ ├── json.go │ ├── string.go │ ├── string_test.go │ ├── util.go │ ├── util_test.go │ └── yaml.go ├── scripts ├── init_mysql.sql ├── init_postgres.sql └── pack │ ├── Makefile │ ├── README.md │ └── start.bat └── web ├── .gitignore ├── css ├── bootstrap.min.css ├── login.css ├── sdk.css └── style.css ├── favicon.ico ├── img ├── guide │ ├── page-add.jpg │ ├── permission-button-visible.jpg │ ├── push-menu-first.jpg │ ├── push-menu-result.jpg │ └── push-menu-second.jpg └── logo.jpg ├── index.html ├── js ├── app.js ├── common.js ├── config.js ├── jquery.min.js ├── login.js ├── md5.min.js ├── preload.js ├── sdk.js ├── store2.min.js └── wizard.js ├── login.html └── page ├── preview.html ├── tpl_preview.html └── wizard.html /.air.conf: -------------------------------------------------------------------------------- 1 | # Config file for [Air](https://github.com/cosmtrek/air) in TOML format 2 | 3 | # Working directory 4 | # . or absolute path, please note that the directories following must be under root 5 | root = "." 6 | # Optional! If `watch_dir` is empty, use `root`. 7 | watch_dir = "" 8 | tmp_dir = "tmp" 9 | 10 | [build] 11 | # Just plain old shell command. You could use `make` as well. 12 | cmd = "wire gen ./internal/app/injector && swag init --generalInfo ./internal/app/swagger.go --output ./internal/app/swagger && go build -o ./cmd/gin-admin/gin-admin ./cmd/gin-admin" 13 | # Binary file yields from `cmd`. 14 | bin = "./cmd/gin-admin/gin-admin" 15 | # Customize binary. 16 | full_bin = "./cmd/gin-admin/gin-admin web -c ./configs/config.toml -m ./configs/model.conf --menu ./configs/menu.yaml --page ./configs/page_manager.yaml --tpl-mall ./configs/tpl_mall.yaml" 17 | # This log file places in your tmp_dir. 18 | log = "air_errors.log" 19 | # Watch these filename extensions. 20 | include_ext = ["go", "toml"] 21 | # Ignore these filename extensions or directories. 22 | exclude_dir = ["assets", "tmp", "data", "docs", "scripts", "vendor", "internal/app/swagger"] 23 | # Ignore files 24 | exclude_file = ["internal/app/injector/wire_gen.go"] 25 | # There's no necessary to trigger build each time file changes if it's too frequency. 26 | delay = 5000 # ms 27 | # Stop running old binary when build errors occur. 28 | stop_on_error = true 29 | # Send Interrupt signal before killing process (windows does not support this feature) 30 | send_interrupt = true 31 | # Delay after sending Interrupt signal 32 | kill_delay = 2000 # ms 33 | 34 | [log] 35 | # Show log time 36 | time = false 37 | 38 | [color] 39 | # Customize each part's color. If no color found, use the raw app log. 40 | main = "magenta" 41 | watcher = "cyan" 42 | build = "yellow" 43 | runner = "green" 44 | 45 | [misc] 46 | # Delete tmp directory on exit 47 | clean_on_exit = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | *.DS_Store 15 | /cmd/gin-amis-admin/gin-amis-admin* 16 | /cmd/gin-amis-admin/data 17 | /release 18 | /data 19 | /internal/app/test/data 20 | /tmp 21 | /vendor 22 | 23 | # IDE configs 24 | .idea 25 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lyric 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: start build 2 | 3 | NOW = $(shell date -u '+%Y%m%d%I%M%S') 4 | 5 | APP = gin-amis-admin 6 | SERVER_BIN = ./cmd/${APP}/${APP} 7 | RELEASE_ROOT = release 8 | RELEASE_SERVER = release/${APP} 9 | 10 | all: start 11 | 12 | build: 13 | @go build -ldflags "-w -s" -o $(SERVER_BIN) ./cmd/${APP} 14 | 15 | build-darwin: 16 | xgo -go go-1.14.x -targets=darwin/amd64 -pkg=cmd/gin-amis-admin/main.go -dest=cmd/${APP} -out=gin-amis-admin . 17 | build-windows-386: 18 | xgo -go go-1.14.x -targets=windows/386 -pkg=cmd/gin-amis-admin/main.go -dest=cmd/${APP} -out=gin-amis-admin . 19 | build-windows-amd64: 20 | xgo -go go-1.14.x -targets=windows/amd64 -pkg=cmd/gin-amis-admin/main.go -dest=cmd/${APP} -out=gin-amis-admin . 21 | 22 | start: 23 | go run cmd/${APP}/main.go web -c ./configs/config.toml -m ./configs/model.conf --menu ./configs/menu.yaml --page ./configs/page_manager.yaml --tpl-mall ./configs/tpl_mall.yaml 24 | 25 | swagger: 26 | swag init --generalInfo ./internal/app/swagger.go --output ./internal/app/swagger 27 | 28 | wire: 29 | wire gen ./internal/app/injector 30 | 31 | test: 32 | @go test -v $(shell go list ./...) 33 | 34 | clean: 35 | rm -rf data release $(SERVER_BIN) ./internal/app/test/data ./cmd/${APP}/data 36 | 37 | pack: build 38 | mkdir -p $(RELEASE_SERVER) 39 | rm -f $(APP)-linux-adm64.tar.gz 40 | cp -r $(SERVER_BIN) configs web $(RELEASE_SERVER) 41 | cp scripts/pack/* $(RELEASE_SERVER) 42 | cd $(RELEASE_ROOT) && tar -zcvf $(APP)-linux-adm64.tar.gz ${APP} && sudo rm -rf ${APP} 43 | pack-darwin: build-darwin 44 | mkdir -p $(RELEASE_SERVER) 45 | rm -f $(APP)-darwin-amd64.tar.gz 46 | cp -r $(SERVER_BIN)-darwin-10.6-amd64 configs web $(RELEASE_SERVER) 47 | cp scripts/pack/* $(RELEASE_SERVER) 48 | mv $(RELEASE_SERVER)/$(APP)-darwin-10.6-amd64 $(RELEASE_SERVER)/$(APP) 49 | cd $(RELEASE_ROOT) && tar -zcvf $(APP)-darwin-amd64.tar.gz ${APP} && sudo rm -rf ${APP} 50 | pack-windows-386: build-windows-386 51 | mkdir -p $(RELEASE_SERVER) 52 | rm -f $(APP)-windows-4.0-386.tar.gz 53 | cp -r $(SERVER_BIN)-windows-4.0-386.exe configs web $(RELEASE_SERVER) 54 | cp scripts/pack/* $(RELEASE_SERVER) 55 | mv $(RELEASE_SERVER)/$(APP)-windows-4.0-386.exe $(RELEASE_SERVER)/$(APP).exe 56 | cd $(RELEASE_ROOT) && tar -zcvf $(APP)-windows-4.0-386.tar.gz ${APP} && sudo rm -rf ${APP} 57 | pack-windows-amd64: build-windows-amd64 58 | mkdir -p $(RELEASE_SERVER) 59 | rm -f $(APP)-windows-4.0-amd64.tar.gz 60 | cp -r $(SERVER_BIN)-windows-4.0-amd64.exe configs web $(RELEASE_SERVER) 61 | cp scripts/pack/* $(RELEASE_SERVER) 62 | mv $(RELEASE_SERVER)/$(APP)-windows-4.0-amd64.exe $(RELEASE_SERVER)/$(APP).exe 63 | cd $(RELEASE_ROOT) && tar -zcvf $(APP)-windows-4.0-amd64.tar.gz ${APP} && sudo rm -rf ${APP} 64 | pack-all: pack pack-darwin pack-windows-386 pack-windows-amd64 65 | -------------------------------------------------------------------------------- /cmd/gin-amis-admin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 9 | "github.com/urfave/cli/v2" 10 | ) 11 | 12 | // VERSION 版本号,可以通过编译的方式指定版本号:go build -ldflags "-X main.VERSION=x.x.x" 13 | var VERSION = "6.4.2" 14 | 15 | func main() { 16 | logger.SetVersion(VERSION) 17 | ctx := logger.NewTraceIDContext(context.Background(), "main") 18 | 19 | app := cli.NewApp() 20 | app.Name = "gin-admin" 21 | app.Version = VERSION 22 | app.Usage = "RBAC scaffolding based on GIN + GORM/MONGO + CASBIN + WIRE." 23 | app.Commands = []*cli.Command{ 24 | newWebCmd(ctx), 25 | } 26 | err := app.Run(os.Args) 27 | if err != nil { 28 | logger.Errorf(ctx, err.Error()) 29 | } 30 | } 31 | 32 | func newWebCmd(ctx context.Context) *cli.Command { 33 | return &cli.Command{ 34 | Name: "web", 35 | Usage: "运行web服务", 36 | Flags: []cli.Flag{ 37 | &cli.StringFlag{ 38 | Name: "conf", 39 | Aliases: []string{"c"}, 40 | Usage: "配置文件(.json,.yaml,.toml)", 41 | Required: true, 42 | }, 43 | &cli.StringFlag{ 44 | Name: "model", 45 | Aliases: []string{"m"}, 46 | Usage: "casbin的访问控制模型(.conf)", 47 | Required: true, 48 | }, 49 | &cli.StringFlag{ 50 | Name: "menu", 51 | Usage: "初始化菜单数据配置文件(.yaml)", 52 | }, 53 | &cli.StringFlag{ 54 | Name: "page", 55 | Usage: "初始化页面管理数据配置文件(.yaml)", 56 | }, 57 | &cli.StringFlag{ 58 | Name: "tpl-mall", 59 | Usage: "初始模板商城数据配置文件(.yaml)", 60 | }, 61 | &cli.StringFlag{ 62 | Name: "www", 63 | Usage: "静态站点目录", 64 | }, 65 | }, 66 | Action: func(c *cli.Context) error { 67 | return app.Run(ctx, 68 | app.SetConfigFile(c.String("conf")), 69 | app.SetModelFile(c.String("model")), 70 | app.SetWWWDir(c.String("www")), 71 | app.SetMenuFile(c.String("menu")), 72 | app.SetPageFile(c.String("page")), 73 | app.SetTplMallFile(c.String("tpl-mall")), 74 | app.SetVersion(VERSION)) 75 | }, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /configs/model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = g(r.sub, p.sub) == true \ 15 | && keyMatch2(r.obj, p.obj) == true \ 16 | && regexMatch(r.act, p.act) == true \ 17 | || r.sub == "root" -------------------------------------------------------------------------------- /gin-amis-admin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/gin-amis-admin -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tanjiancheng/gin-amis-admin 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/LyricTian/captcha v1.1.0 7 | github.com/LyricTian/gzip v0.1.1 8 | github.com/LyricTian/queue v1.2.0 9 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 10 | github.com/bwmarrin/snowflake v0.3.0 11 | github.com/casbin/casbin/v2 v2.4.1 12 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect 13 | github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc // indirect 14 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 15 | github.com/elliotchance/orderedmap v1.3.0 16 | github.com/fatih/camelcase v1.0.0 // indirect 17 | github.com/fatih/structs v1.1.0 // indirect 18 | github.com/fsnotify/fsnotify v1.4.9 // indirect 19 | github.com/gin-contrib/cors v1.3.1 20 | github.com/gin-gonic/gin v1.6.3 21 | github.com/go-openapi/spec v0.19.7 // indirect 22 | github.com/go-openapi/swag v0.19.9 // indirect 23 | github.com/go-redis/redis v6.15.7+incompatible 24 | github.com/go-redis/redis_rate v6.5.0+incompatible 25 | github.com/golang/protobuf v1.4.1 // indirect 26 | github.com/google/gops v0.3.8 27 | github.com/google/uuid v1.1.1 28 | github.com/google/wire v0.4.0 29 | github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 30 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a 31 | github.com/jinzhu/gorm v1.9.14 32 | github.com/jinzhu/now v1.1.1 // indirect 33 | github.com/jmespath/go-jmespath v0.3.0 34 | github.com/json-iterator/go v1.1.9 35 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect 36 | github.com/klauspost/compress v1.10.5 // indirect 37 | github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 38 | github.com/kr/text v0.2.0 // indirect 39 | github.com/lib/pq v1.5.2 // indirect 40 | github.com/mailru/easyjson v0.7.1 // indirect 41 | github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect 42 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 43 | github.com/onsi/ginkgo v1.12.0 // indirect 44 | github.com/onsi/gomega v1.10.0 // indirect 45 | github.com/pkg/errors v0.9.1 46 | github.com/sirupsen/logrus v1.6.0 47 | github.com/stretchr/testify v1.5.1 48 | github.com/swaggo/gin-swagger v1.2.0 49 | github.com/swaggo/swag v1.6.5 50 | github.com/tidwall/buntdb v1.1.2 51 | github.com/tidwall/gjson v1.6.0 // indirect 52 | github.com/tidwall/pretty v1.0.1 // indirect 53 | github.com/urfave/cli/v2 v2.2.0 54 | github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 // indirect 55 | go.mongodb.org/mongo-driver v1.3.3 56 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect 57 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f // indirect 58 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect 59 | golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f // indirect 60 | golang.org/x/text v0.3.3 // indirect 61 | golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 62 | golang.org/x/tools v0.0.0-20200511202723-1762287ae9dd // indirect 63 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 64 | gopkg.in/yaml.v2 v2.2.8 65 | ) 66 | -------------------------------------------------------------------------------- /internal/app/api/a_app.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/google/wire" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 10 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 11 | "time" 12 | ) 13 | 14 | var AppSet = wire.NewSet(wire.Struct(new(App), "*")) 15 | 16 | // 应用相关接口 17 | type App struct { 18 | AppBll bll.IApp 19 | MenuBll bll.IMenu 20 | PageBll bll.IPageManager 21 | SettingBll bll.ISetting 22 | GPlatformBll bll.IGPlatform 23 | } 24 | 25 | //初始化应用相关资源 26 | func (a *App) Init(c *gin.Context) { 27 | 28 | var params schema.AppQueryParam 29 | if err := ginplus.ParseJSON(c, ¶ms); err != nil { 30 | ginplus.ResError(c, err) 31 | return 32 | } 33 | appId := params.AppId 34 | //判断应用是否已经初始化 35 | exist, err := a.AppBll.Query(c, appId) 36 | if exist { 37 | ginplus.ResCustomError(c, fmt.Errorf("该应用已经初始化过,无须重新初始化!")) 38 | return 39 | } 40 | 41 | err = a.AppBll.Init(c, appId) 42 | if err != nil { 43 | ginplus.ResCustomError(c, err) 44 | return 45 | } 46 | err = a.MenuBll.InitData(c, config.C.Menu.Data) 47 | if err != nil { 48 | ginplus.ResCustomError(c, err) 49 | return 50 | } 51 | err = a.PageBll.InitData(c, config.C.Page.Data) 52 | if err != nil { 53 | ginplus.ResCustomError(c, err) 54 | return 55 | } 56 | 57 | //插入到全局平台表 58 | err = a.GPlatformBll.Create(c, schema.GPlatform{ 59 | AppID: appId, 60 | Name: params.PlatformName, 61 | Status: 1, 62 | CreateTime: time.Now().Unix(), 63 | }) 64 | if err != nil { 65 | ginplus.ResCustomError(c, err) 66 | return 67 | } 68 | 69 | //更新平台信息 70 | err = a.SettingBll.Truncate(c) 71 | if err != nil { 72 | ginplus.ResCustomError(c, err) 73 | return 74 | } 75 | var settings schema.Settings 76 | settings = append(settings, &schema.Setting{ 77 | Key: "platform_name", 78 | Value: params.PlatformName, 79 | CreatedAt: time.Time{}, 80 | UpdatedAt: time.Time{}, 81 | }) 82 | 83 | settings = append(settings, &schema.Setting{ 84 | Key: "platform_logo", 85 | Value: params.PlatformLogo, 86 | CreatedAt: time.Time{}, 87 | UpdatedAt: time.Time{}, 88 | }) 89 | for _, setting := range settings { 90 | err := a.SettingBll.Create(c, *setting) 91 | if err != nil { 92 | ginplus.ResCustomError(c, err) 93 | return 94 | } 95 | } 96 | 97 | ginplus.ResCustomSuccess(c, appId) 98 | } 99 | 100 | func (a *App) Query(c *gin.Context) { 101 | appId := c.Param("id") 102 | if len(appId) <= 0 { 103 | appId = ginplus.GetDefaultAppId() 104 | } 105 | exist, err := a.AppBll.Query(c, appId) 106 | if err != nil { 107 | ginplus.ResCustomError(c, err) 108 | return 109 | } 110 | ginplus.ResCustomSuccess(c, exist) 111 | } 112 | -------------------------------------------------------------------------------- /internal/app/api/a_demo.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/gin-gonic/gin" 8 | "github.com/google/wire" 9 | ) 10 | 11 | // DemoSet 注入Demo 12 | var DemoSet = wire.NewSet(wire.Struct(new(Demo), "*")) 13 | 14 | // Demo 示例程序 15 | type Demo struct { 16 | DemoBll bll.IDemo 17 | } 18 | 19 | // Query 查询数据 20 | func (a *Demo) Query(c *gin.Context) { 21 | ctx := c.Request.Context() 22 | var params schema.DemoQueryParam 23 | if err := ginplus.ParseQuery(c, ¶ms); err != nil { 24 | ginplus.ResError(c, err) 25 | return 26 | } 27 | 28 | params.Pagination = true 29 | result, err := a.DemoBll.Query(ctx, params) 30 | if err != nil { 31 | ginplus.ResError(c, err) 32 | return 33 | } 34 | 35 | ginplus.ResPage(c, result.Data, result.PageResult) 36 | } 37 | 38 | // Get 查询指定数据 39 | func (a *Demo) Get(c *gin.Context) { 40 | ctx := c.Request.Context() 41 | item, err := a.DemoBll.Get(ctx, c.Param("id")) 42 | if err != nil { 43 | ginplus.ResError(c, err) 44 | return 45 | } 46 | ginplus.ResSuccess(c, item) 47 | } 48 | 49 | // Create 创建数据 50 | func (a *Demo) Create(c *gin.Context) { 51 | ctx := c.Request.Context() 52 | var item schema.Demo 53 | if err := ginplus.ParseJSON(c, &item); err != nil { 54 | ginplus.ResError(c, err) 55 | return 56 | } 57 | 58 | item.Creator = ginplus.GetUserID(c) 59 | result, err := a.DemoBll.Create(ctx, item) 60 | if err != nil { 61 | ginplus.ResError(c, err) 62 | return 63 | } 64 | ginplus.ResSuccess(c, result) 65 | } 66 | 67 | // Update 更新数据 68 | func (a *Demo) Update(c *gin.Context) { 69 | ctx := c.Request.Context() 70 | var item schema.Demo 71 | if err := ginplus.ParseJSON(c, &item); err != nil { 72 | ginplus.ResError(c, err) 73 | return 74 | } 75 | 76 | err := a.DemoBll.Update(ctx, c.Param("id"), item) 77 | if err != nil { 78 | ginplus.ResError(c, err) 79 | return 80 | } 81 | ginplus.ResOK(c) 82 | } 83 | 84 | // Delete 删除数据 85 | func (a *Demo) Delete(c *gin.Context) { 86 | ctx := c.Request.Context() 87 | err := a.DemoBll.Delete(ctx, c.Param("id")) 88 | if err != nil { 89 | ginplus.ResError(c, err) 90 | return 91 | } 92 | ginplus.ResOK(c) 93 | } 94 | 95 | // Enable 启用数据 96 | func (a *Demo) Enable(c *gin.Context) { 97 | ctx := c.Request.Context() 98 | err := a.DemoBll.UpdateStatus(ctx, c.Param("id"), 1) 99 | if err != nil { 100 | ginplus.ResError(c, err) 101 | return 102 | } 103 | ginplus.ResOK(c) 104 | } 105 | 106 | // Disable 禁用数据 107 | func (a *Demo) Disable(c *gin.Context) { 108 | ctx := c.Request.Context() 109 | err := a.DemoBll.UpdateStatus(ctx, c.Param("id"), 2) 110 | if err != nil { 111 | ginplus.ResError(c, err) 112 | return 113 | } 114 | ginplus.ResOK(c) 115 | } 116 | -------------------------------------------------------------------------------- /internal/app/api/a_page_version_history.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/gin-gonic/gin" 9 | "github.com/google/wire" 10 | ) 11 | 12 | // PageVersionHistorySet 注入PageVersionHistory 13 | var PageVersionHistorySet = wire.NewSet(wire.Struct(new(PageVersionHistory), "*")) 14 | 15 | // PageVersionHistory 用户管理 16 | type PageVersionHistory struct { 17 | PageVersionHistoryBll bll.IPageVersionHistory 18 | } 19 | 20 | // Query 查询数据 21 | func (a *PageVersionHistory) Query(c *gin.Context) { 22 | ctx := c.Request.Context() 23 | var params schema.PageVersionHistoryQueryParam 24 | if err := ginplus.ParseQuery(c, ¶ms); err != nil { 25 | ginplus.ResError(c, err) 26 | return 27 | } 28 | params.Pagination = true 29 | result, err := a.PageVersionHistoryBll.Query(ctx, params) 30 | if err != nil { 31 | ginplus.ResError(c, err) 32 | return 33 | } 34 | 35 | listData := map[string]interface{}{ 36 | "rows": result.Data, 37 | "count": result.PageResult.Total, 38 | } 39 | ginplus.ResCustomSuccess(c, listData) 40 | } 41 | 42 | 43 | // Get 查询指定数据 44 | func (a *PageVersionHistory) Get(c *gin.Context) { 45 | ctx := c.Request.Context() 46 | item, err := a.PageVersionHistoryBll.Get(ctx, c.Param("id")) 47 | if err != nil { 48 | ginplus.ResError(c, err) 49 | return 50 | } 51 | var pageManager schema.PageManager 52 | err = json.Unmarshal([]byte(item.PageManagerInfo), &pageManager) 53 | if err != nil { 54 | ginplus.ResError(c, err) 55 | return 56 | } 57 | item.Name = pageManager.Name 58 | item.PageCreateTime = pageManager.CreateTime 59 | ginplus.ResCustomSuccess(c, item) 60 | } 61 | -------------------------------------------------------------------------------- /internal/app/api/a_user.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 10 | "github.com/gin-gonic/gin" 11 | "github.com/google/wire" 12 | ) 13 | 14 | // UserSet 注入User 15 | var UserSet = wire.NewSet(wire.Struct(new(User), "*")) 16 | 17 | // User 用户管理 18 | type User struct { 19 | UserBll bll.IUser 20 | } 21 | 22 | // Query 查询数据 23 | func (a *User) Query(c *gin.Context) { 24 | ctx := c.Request.Context() 25 | var params schema.UserQueryParam 26 | if err := ginplus.ParseQuery(c, ¶ms); err != nil { 27 | ginplus.ResError(c, err) 28 | return 29 | } 30 | if v := c.Query("roleIDs"); v != "" { 31 | params.RoleIDs = strings.Split(v, ",") 32 | } 33 | 34 | params.Pagination = true 35 | result, err := a.UserBll.QueryShow(ctx, params) 36 | if err != nil { 37 | ginplus.ResError(c, err) 38 | return 39 | } 40 | ginplus.ResPage(c, result.Data, result.PageResult) 41 | } 42 | 43 | // Get 查询指定数据 44 | func (a *User) Get(c *gin.Context) { 45 | ctx := c.Request.Context() 46 | item, err := a.UserBll.Get(ctx, c.Param("id")) 47 | if err != nil { 48 | ginplus.ResError(c, err) 49 | return 50 | } 51 | ginplus.ResSuccess(c, item.CleanSecure()) 52 | } 53 | 54 | // Create 创建数据 55 | func (a *User) Create(c *gin.Context) { 56 | ctx := c.Request.Context() 57 | var item schema.User 58 | if err := ginplus.ParseJSON(c, &item); err != nil { 59 | ginplus.ResError(c, err) 60 | return 61 | } else if item.Password == "" { 62 | ginplus.ResError(c, errors.New400Response("密码不能为空")) 63 | return 64 | } 65 | 66 | item.Creator = ginplus.GetUserID(c) 67 | result, err := a.UserBll.Create(ctx, item) 68 | if err != nil { 69 | ginplus.ResError(c, err) 70 | return 71 | } 72 | ginplus.ResSuccess(c, result) 73 | } 74 | 75 | // Update 更新数据 76 | func (a *User) Update(c *gin.Context) { 77 | ctx := c.Request.Context() 78 | var item schema.User 79 | if err := ginplus.ParseJSON(c, &item); err != nil { 80 | ginplus.ResError(c, err) 81 | return 82 | } 83 | 84 | err := a.UserBll.Update(ctx, c.Param("id"), item) 85 | if err != nil { 86 | ginplus.ResError(c, err) 87 | return 88 | } 89 | ginplus.ResOK(c) 90 | } 91 | 92 | // Delete 删除数据 93 | func (a *User) Delete(c *gin.Context) { 94 | ctx := c.Request.Context() 95 | err := a.UserBll.Delete(ctx, c.Param("id")) 96 | if err != nil { 97 | ginplus.ResError(c, err) 98 | return 99 | } 100 | ginplus.ResOK(c) 101 | } 102 | 103 | // Enable 启用数据 104 | func (a *User) Enable(c *gin.Context) { 105 | ctx := c.Request.Context() 106 | err := a.UserBll.UpdateStatus(ctx, c.Param("id"), 1) 107 | if err != nil { 108 | ginplus.ResError(c, err) 109 | return 110 | } 111 | ginplus.ResOK(c) 112 | } 113 | 114 | // Disable 禁用数据 115 | func (a *User) Disable(c *gin.Context) { 116 | ctx := c.Request.Context() 117 | err := a.UserBll.UpdateStatus(ctx, c.Param("id"), 2) 118 | if err != nil { 119 | ginplus.ResError(c, err) 120 | return 121 | } 122 | ginplus.ResOK(c) 123 | } 124 | -------------------------------------------------------------------------------- /internal/app/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/google/wire" 4 | 5 | // APISet 注入api 6 | var APISet = wire.NewSet( 7 | DemoSet, 8 | LoginSet, 9 | MenuSet, 10 | RoleSet, 11 | UserSet, 12 | PageManagerSet, 13 | SettingSet, 14 | AppSet, 15 | ) 16 | -------------------------------------------------------------------------------- /internal/app/api/mock/mock.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import "github.com/google/wire" 4 | 5 | // MockSet 注入mock 6 | var MockSet = wire.NewSet( 7 | DemoSet, 8 | LoginSet, 9 | MenuSet, 10 | RoleSet, 11 | UserSet, 12 | ) 13 | -------------------------------------------------------------------------------- /internal/app/bll/b_app.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type IApp interface { 8 | // 初始化数据 9 | Init(ctx context.Context, appId string) error 10 | // 查询是否初始化过 11 | Query(ctx context.Context, appId string) (bool, error) 12 | } 13 | -------------------------------------------------------------------------------- /internal/app/bll/b_demo.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IDemo demo业务逻辑接口 10 | type IDemo interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.DemoQueryParam, opts ...schema.DemoQueryOptions) (*schema.DemoQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.DemoQueryOptions) (*schema.Demo, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.Demo) (*schema.IDResult, error) 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.Demo) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 更新状态 22 | UpdateStatus(ctx context.Context, id string, status int) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/bll/b_g_platform.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IGPlatform 业务逻辑接口 10 | type IGPlatform interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.GPlatformQueryParam, opts ...schema.GPlatformQueryOptions) (*schema.GPlatformQueryResult, error) 13 | // 检查appId是否合法 14 | Check(ctx context.Context, appId string) error 15 | // 查询指定数据 16 | Get(ctx context.Context, id string, opts ...schema.GPlatformQueryOptions) (*schema.GPlatform, error) 17 | // 获取select的查询配置 18 | GetOptions(ctx context.Context) (*schema.GPlatformSelectOptions, error) 19 | // 根据app_id查询对应的数据 20 | GetByAppId(ctx context.Context, appId string) (*schema.GPlatform, error) 21 | // 创建数据 22 | Create(ctx context.Context, item schema.GPlatform) error 23 | // 更新数据 24 | Update(ctx context.Context, id string, item schema.GPlatform) error 25 | // 删除数据 26 | Delete(ctx context.Context, id string) error 27 | // 更新状态 28 | UpdateStatus(ctx context.Context, id string, status int) error 29 | } 30 | -------------------------------------------------------------------------------- /internal/app/bll/b_g_tpl_mall.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IGTplMall 业务逻辑接口 10 | type IGTplMall interface { 11 | // 初始化商城数据 12 | InitData(ctx context.Context, dataFile string) error 13 | // 查询数据 14 | Query(ctx context.Context, params schema.GTplMallQueryParam, opts ...schema.GTplMallQueryOptions) (*schema.GTplMallQueryResult, error) 15 | // 查询指定数据 16 | Get(ctx context.Context, id string, opts ...schema.GTplMallQueryOptions) (*schema.GTplMall, error) 17 | // 根据app_id查询对应的数据 18 | GetByIdentify(ctx context.Context, identify string) (*schema.GTplMall, error) 19 | // 创建数据 20 | Create(ctx context.Context, item schema.GTplMall) error 21 | // 更新数据 22 | Update(ctx context.Context, id string, item schema.GTplMall) error 23 | // 删除数据 24 | Delete(ctx context.Context, id string) error 25 | // 更新状态 26 | UpdateStatus(ctx context.Context, id string, status int) error 27 | } 28 | -------------------------------------------------------------------------------- /internal/app/bll/b_login.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | ) 9 | 10 | // ILogin 登录业务逻辑接口 11 | type ILogin interface { 12 | // 获取图形验证码信息 13 | GetCaptcha(ctx context.Context, length int) (*schema.LoginCaptcha, error) 14 | // 生成并响应图形验证码 15 | ResCaptcha(ctx context.Context, w http.ResponseWriter, captchaID string, width, height int) error 16 | // 登录验证 17 | Verify(ctx context.Context, userName, password string) (*schema.User, error) 18 | // 生成令牌 19 | GenerateToken(ctx context.Context, userID string) (*schema.LoginTokenInfo, error) 20 | // 销毁令牌 21 | DestroyToken(ctx context.Context, tokenString string) error 22 | // 获取用户登录信息 23 | GetLoginInfo(ctx context.Context, userID string) (*schema.UserLoginInfo, error) 24 | // 查询用户的权限菜单树 25 | QueryUserMenuTree(ctx context.Context, userID string) (schema.MenuTrees, error) 26 | // 查询用户的权限列表 27 | Permissions(ctx context.Context, userID string) (schema.Permissions, error) 28 | // 更新用户登录密码 29 | UpdatePassword(ctx context.Context, userID string, params schema.UpdatePasswordParam) error 30 | } 31 | -------------------------------------------------------------------------------- /internal/app/bll/b_menu.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IMenu 菜单管理业务逻辑接口 10 | type IMenu interface { 11 | // 初始化菜单数据 12 | InitData(ctx context.Context, dataFile string) error 13 | // 查询数据 14 | Query(ctx context.Context, params schema.MenuQueryParam, opts ...schema.MenuQueryOptions) (*schema.MenuQueryResult, error) 15 | // 查询指定数据 16 | Get(ctx context.Context, id string, opts ...schema.MenuQueryOptions) (*schema.Menu, error) 17 | // 根据路由查询对应的数据 18 | GetByRouter(ctx context.Context, router string) (*schema.Menu, error) 19 | // 创建数据 20 | Create(ctx context.Context, item schema.Menu) (*schema.IDResult, error) 21 | // 更新数据 22 | Update(ctx context.Context, id string, item schema.Menu) error 23 | // 删除数据 24 | Delete(ctx context.Context, id string) error 25 | // 更新状态 26 | UpdateStatus(ctx context.Context, id string, status int) error 27 | } 28 | -------------------------------------------------------------------------------- /internal/app/bll/b_page_manager.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 6 | ) 7 | 8 | // IPageManager 页面管理业务逻辑接口 9 | type IPageManager interface { 10 | // 初始化菜单数据 11 | InitData(ctx context.Context, dataFile string) error 12 | // 查询数据 13 | Query(ctx context.Context, params schema.PageManagerQueryParam, opts ...schema.PageManagerQueryOptions) (*schema.PageManagerQueryResult, error) 14 | // 根据路由查询指定数据 15 | GetByRoute(ctx context.Context, id string, opts ...schema.PageManagerQueryOptions) (*schema.PageManager, error) 16 | // 查询指定数据 17 | Get(ctx context.Context, id string, opts ...schema.PageManagerQueryOptions) (*schema.PageManager, error) 18 | // 创建数据 19 | Create(ctx context.Context, item schema.PageManager) error 20 | // 更新数据 21 | Update(ctx context.Context, id string, item schema.PageManager) error 22 | // 删除数据 23 | Delete(ctx context.Context, id string) error 24 | // 更新状态 25 | UpdateStatus(ctx context.Context, id string, status int) error 26 | // 获取最后一条记录的ide 27 | GetLastId(ctx context.Context) (int, error) 28 | // 获取历史版本 29 | History(ctx context.Context, params schema.PageVersionHistoryQueryParam, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistoryQueryResult, error) 30 | // 根据版本信息回滚 31 | Revert(ctx context.Context, pageVersionHistoryId string) error 32 | } 33 | -------------------------------------------------------------------------------- /internal/app/bll/b_page_version_history.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 6 | ) 7 | 8 | // IPageVersionHistory 页面历史管理业务逻辑接口 9 | type IPageVersionHistory interface { 10 | // 查询数据 11 | Query(ctx context.Context, params schema.PageVersionHistoryQueryParam, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistoryQueryResult, error) 12 | // 查询指定数据 13 | Get(ctx context.Context, id string, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistory, error) 14 | } 15 | -------------------------------------------------------------------------------- /internal/app/bll/b_role.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IRole 角色管理业务逻辑接口 10 | type IRole interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.RoleQueryParam, opts ...schema.RoleQueryOptions) (*schema.RoleQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.RoleQueryOptions) (*schema.Role, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.Role) (*schema.IDResult, error) 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.Role) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 更新状态 22 | UpdateStatus(ctx context.Context, id string, status int) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/bll/b_setting.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // ISetting demo业务逻辑接口 10 | type ISetting interface { 11 | // 查询数据 12 | Query(ctx context.Context) (*schema.SettingQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string) (*schema.Setting, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.Setting) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.Setting) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 清空表 22 | Truncate(ctx context.Context) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/bll/b_user.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IUser 用户管理业务逻辑接口 10 | type IUser interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.UserQueryParam, opts ...schema.UserQueryOptions) (*schema.UserQueryResult, error) 13 | // 查询显示项数据 14 | QueryShow(ctx context.Context, params schema.UserQueryParam, opts ...schema.UserQueryOptions) (*schema.UserShowQueryResult, error) 15 | // 查询指定数据 16 | Get(ctx context.Context, id string, opts ...schema.UserQueryOptions) (*schema.User, error) 17 | // 创建数据 18 | Create(ctx context.Context, item schema.User) (*schema.IDResult, error) 19 | // 更新数据 20 | Update(ctx context.Context, id string, item schema.User) error 21 | // 删除数据 22 | Delete(ctx context.Context, id string) error 23 | // 更新状态 24 | UpdateStatus(ctx context.Context, id string, status int) error 25 | } 26 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/b_app.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 8 | "github.com/google/wire" 9 | ) 10 | 11 | var _ bll.IApp = (*App)(nil) 12 | 13 | var AppSet = wire.NewSet(wire.Struct(new(App), "*"), wire.Bind(new(bll.IApp), new(*App))) 14 | 15 | type App struct { 16 | AppModel model.IApp 17 | } 18 | 19 | func (a *App) Init(ctx context.Context, appId string) error { 20 | return a.AppModel.Init(ctx, appId) 21 | } 22 | 23 | func (a *App) Query(ctx context.Context, appId string) (bool, error) { 24 | return a.AppModel.Query(ctx, appId) 25 | } 26 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/b_casbin.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 8 | "github.com/casbin/casbin/v2" 9 | ) 10 | 11 | var chCasbinPolicy chan *chCasbinPolicyItem 12 | 13 | type chCasbinPolicyItem struct { 14 | ctx context.Context 15 | e *casbin.SyncedEnforcer 16 | } 17 | 18 | func init() { 19 | chCasbinPolicy = make(chan *chCasbinPolicyItem, 1) 20 | go func() { 21 | for item := range chCasbinPolicy { 22 | err := item.e.LoadPolicy() 23 | if err != nil { 24 | logger.Errorf(item.ctx, "The load casbin policy error: %s", err.Error()) 25 | } 26 | } 27 | }() 28 | } 29 | 30 | // LoadCasbinPolicy 异步加载casbin权限策略 31 | func LoadCasbinPolicy(ctx context.Context, e *casbin.SyncedEnforcer) { 32 | if !config.C.Casbin.Enable { 33 | return 34 | } 35 | 36 | if len(chCasbinPolicy) > 0 { 37 | logger.Infof(ctx, "The load casbin policy is already in the wait queue") 38 | return 39 | } 40 | 41 | chCasbinPolicy <- &chCasbinPolicyItem{ 42 | ctx: ctx, 43 | e: e, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/b_common.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/icontext" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 8 | ) 9 | 10 | // TransFunc 定义事务执行函数 11 | type TransFunc func(context.Context) error 12 | 13 | // ExecTrans 执行事务 14 | func ExecTrans(ctx context.Context, transModel model.ITrans, fn TransFunc) error { 15 | return transModel.Exec(ctx, fn) 16 | } 17 | 18 | // ExecTransWithLock 执行事务(加锁) 19 | func ExecTransWithLock(ctx context.Context, transModel model.ITrans, fn TransFunc) error { 20 | if !icontext.FromTransLock(ctx) { 21 | ctx = icontext.NewTransLock(ctx) 22 | } 23 | return ExecTrans(ctx, transModel, fn) 24 | } 25 | 26 | // NewNoTrans 不使用事务执行 27 | func NewNoTrans(ctx context.Context) context.Context { 28 | if !icontext.FromNoTrans(ctx) { 29 | return icontext.NewNoTrans(ctx) 30 | } 31 | return ctx 32 | } 33 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/b_demo.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/iutil" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 10 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 11 | "github.com/google/wire" 12 | ) 13 | 14 | var _ bll.IDemo = (*Demo)(nil) 15 | 16 | // DemoSet 注入Demo 17 | var DemoSet = wire.NewSet(wire.Struct(new(Demo), "*"), wire.Bind(new(bll.IDemo), new(*Demo))) 18 | 19 | // Demo 示例程序 20 | type Demo struct { 21 | DemoModel model.IDemo 22 | } 23 | 24 | // Query 查询数据 25 | func (a *Demo) Query(ctx context.Context, params schema.DemoQueryParam, opts ...schema.DemoQueryOptions) (*schema.DemoQueryResult, error) { 26 | return a.DemoModel.Query(ctx, params, opts...) 27 | } 28 | 29 | // Get 查询指定数据 30 | func (a *Demo) Get(ctx context.Context, id string, opts ...schema.DemoQueryOptions) (*schema.Demo, error) { 31 | item, err := a.DemoModel.Get(ctx, id, opts...) 32 | if err != nil { 33 | return nil, err 34 | } else if item == nil { 35 | return nil, errors.ErrNotFound 36 | } 37 | 38 | return item, nil 39 | } 40 | 41 | func (a *Demo) checkCode(ctx context.Context, code string) error { 42 | result, err := a.DemoModel.Query(ctx, schema.DemoQueryParam{ 43 | PaginationParam: schema.PaginationParam{ 44 | OnlyCount: true, 45 | }, 46 | Code: code, 47 | }) 48 | if err != nil { 49 | return err 50 | } else if result.PageResult.Total > 0 { 51 | return errors.New400Response("编号已经存在") 52 | } 53 | 54 | return nil 55 | } 56 | 57 | // Create 创建数据 58 | func (a *Demo) Create(ctx context.Context, item schema.Demo) (*schema.IDResult, error) { 59 | err := a.checkCode(ctx, item.Code) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | item.ID = iutil.NewID() 65 | err = a.DemoModel.Create(ctx, item) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | return schema.NewIDResult(item.ID), nil 71 | } 72 | 73 | // Update 更新数据 74 | func (a *Demo) Update(ctx context.Context, id string, item schema.Demo) error { 75 | oldItem, err := a.DemoModel.Get(ctx, id) 76 | if err != nil { 77 | return err 78 | } else if oldItem == nil { 79 | return errors.ErrNotFound 80 | } else if oldItem.Code != item.Code { 81 | if err := a.checkCode(ctx, item.Code); err != nil { 82 | return err 83 | } 84 | } 85 | item.ID = oldItem.ID 86 | item.Creator = oldItem.Creator 87 | item.CreatedAt = oldItem.CreatedAt 88 | 89 | return a.DemoModel.Update(ctx, id, item) 90 | } 91 | 92 | // Delete 删除数据 93 | func (a *Demo) Delete(ctx context.Context, id string) error { 94 | oldItem, err := a.DemoModel.Get(ctx, id) 95 | if err != nil { 96 | return err 97 | } else if oldItem == nil { 98 | return errors.ErrNotFound 99 | } 100 | 101 | return a.DemoModel.Delete(ctx, id) 102 | } 103 | 104 | // UpdateStatus 更新状态 105 | func (a *Demo) UpdateStatus(ctx context.Context, id string, status int) error { 106 | oldItem, err := a.DemoModel.Get(ctx, id) 107 | if err != nil { 108 | return err 109 | } else if oldItem == nil { 110 | return errors.ErrNotFound 111 | } 112 | 113 | return a.DemoModel.UpdateStatus(ctx, id, status) 114 | } 115 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/b_page_version_history.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 9 | "github.com/google/wire" 10 | ) 11 | 12 | var _ bll.IPageVersionHistory = (*PageVersionHistory)(nil) 13 | 14 | // PageVersionHistorySet 注入PageVersionHistory 15 | var PageVersionHistorySet = wire.NewSet(wire.Struct(new(PageVersionHistory), "*"), wire.Bind(new(bll.IPageVersionHistory), new(*PageVersionHistory))) 16 | 17 | // 页面管理 18 | type PageVersionHistory struct { 19 | TransModel model.ITrans 20 | PageVersionHistoryModel model.IPageVersionHistory 21 | } 22 | 23 | // 查询数据 24 | func (a *PageVersionHistory) Query(ctx context.Context, params schema.PageVersionHistoryQueryParam, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistoryQueryResult, error) { 25 | return a.PageVersionHistoryModel.Query(ctx, params, opts...) 26 | } 27 | 28 | // 查询指定数据 29 | func (a *PageVersionHistory) Get(ctx context.Context, id string, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistory, error) { 30 | item, err := a.PageVersionHistoryModel.Get(ctx, id, opts...) 31 | if err != nil { 32 | return nil, err 33 | } else if item == nil { 34 | return nil, errors.ErrNotFound 35 | } 36 | 37 | return item, nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/b_setting.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 10 | "github.com/google/wire" 11 | ) 12 | 13 | var _ bll.ISetting = (*Setting)(nil) 14 | 15 | // SettingSet 注入Setting 16 | var SettingSet = wire.NewSet(wire.Struct(new(Setting), "*"), wire.Bind(new(bll.ISetting), new(*Setting))) 17 | 18 | // Setting 示例程序 19 | type Setting struct { 20 | TransModel model.ITrans 21 | SettingModel model.ISetting 22 | } 23 | 24 | // Query 查询数据 25 | func (a *Setting) Query(ctx context.Context) (*schema.SettingQueryResult, error) { 26 | return a.SettingModel.Query(ctx) 27 | } 28 | 29 | // Get 查询指定数据 30 | func (a *Setting) Get(ctx context.Context, id string) (*schema.Setting, error) { 31 | item, err := a.SettingModel.Get(ctx, id) 32 | if err != nil { 33 | return nil, err 34 | } else if item == nil { 35 | return nil, errors.ErrNotFound 36 | } 37 | 38 | return item, nil 39 | } 40 | 41 | // Create 创建数据 42 | func (a *Setting) Create(ctx context.Context, item schema.Setting) error { 43 | err := a.SettingModel.Create(ctx, item) 44 | if err != nil { 45 | return err 46 | } 47 | return nil 48 | } 49 | 50 | // Update 更新数据 51 | func (a *Setting) Update(ctx context.Context, id string, item schema.Setting) error { 52 | oldItem, err := a.SettingModel.Get(ctx, id) 53 | if err != nil { 54 | return err 55 | } else if oldItem == nil { 56 | return errors.ErrNotFound 57 | } 58 | item.CreatedAt = oldItem.CreatedAt 59 | 60 | return a.SettingModel.Update(ctx, id, item) 61 | } 62 | 63 | // Delete 删除数据 64 | func (a *Setting) Delete(ctx context.Context, id string) error { 65 | oldItem, err := a.SettingModel.Get(ctx, id) 66 | if err != nil { 67 | return err 68 | } else if oldItem == nil { 69 | return errors.ErrNotFound 70 | } 71 | 72 | return a.SettingModel.Delete(ctx, id) 73 | } 74 | 75 | func (a *Setting) Truncate(ctx context.Context) error { 76 | return a.SettingModel.Truncate(ctx) 77 | } 78 | -------------------------------------------------------------------------------- /internal/app/bll/impl/bll/bll.go: -------------------------------------------------------------------------------- 1 | package bll 2 | 3 | import "github.com/google/wire" 4 | 5 | // BllSet bll注入 6 | var BllSet = wire.NewSet( 7 | DemoSet, 8 | LoginSet, 9 | MenuSet, 10 | RoleSet, 11 | UserSet, 12 | PageManagerSet, 13 | SettingSet, 14 | AppSet, 15 | ) 16 | -------------------------------------------------------------------------------- /internal/app/icontext/context.go: -------------------------------------------------------------------------------- 1 | package icontext 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // 定义全局上下文中的键 8 | type ( 9 | transCtx struct{} 10 | noTransCtx struct{} 11 | transLockCtx struct{} 12 | userIDCtx struct{} 13 | traceIDCtx struct{} 14 | ) 15 | 16 | // NewTrans 创建事务的上下文 17 | func NewTrans(ctx context.Context, trans interface{}) context.Context { 18 | return context.WithValue(ctx, transCtx{}, trans) 19 | } 20 | 21 | // FromTrans 从上下文中获取事务 22 | func FromTrans(ctx context.Context) (interface{}, bool) { 23 | v := ctx.Value(transCtx{}) 24 | return v, v != nil 25 | } 26 | 27 | // NewNoTrans 创建不使用事务的上下文 28 | func NewNoTrans(ctx context.Context) context.Context { 29 | return context.WithValue(ctx, noTransCtx{}, true) 30 | } 31 | 32 | // FromNoTrans 从上下文中获取不使用事务标识 33 | func FromNoTrans(ctx context.Context) bool { 34 | v := ctx.Value(noTransCtx{}) 35 | return v != nil && v.(bool) 36 | } 37 | 38 | // NewTransLock 创建事务锁的上下文 39 | func NewTransLock(ctx context.Context) context.Context { 40 | return context.WithValue(ctx, transLockCtx{}, true) 41 | } 42 | 43 | // FromTransLock 从上下文中获取事务锁 44 | func FromTransLock(ctx context.Context) bool { 45 | v := ctx.Value(transLockCtx{}) 46 | return v != nil && v.(bool) 47 | } 48 | 49 | // NewUserID 创建用户ID的上下文 50 | func NewUserID(ctx context.Context, userID string) context.Context { 51 | return context.WithValue(ctx, userIDCtx{}, userID) 52 | } 53 | 54 | // FromUserID 从上下文中获取用户ID 55 | func FromUserID(ctx context.Context) (string, bool) { 56 | v := ctx.Value(userIDCtx{}) 57 | if v != nil { 58 | if s, ok := v.(string); ok { 59 | return s, s != "" 60 | } 61 | } 62 | return "", false 63 | } 64 | 65 | // NewTraceID 创建追踪ID的上下文 66 | func NewTraceID(ctx context.Context, traceID string) context.Context { 67 | return context.WithValue(ctx, traceIDCtx{}, traceID) 68 | } 69 | 70 | // FromTraceID 从上下文中获取追踪ID 71 | func FromTraceID(ctx context.Context) (string, bool) { 72 | v := ctx.Value(traceIDCtx{}) 73 | if v != nil { 74 | if s, ok := v.(string); ok { 75 | return s, s != "" 76 | } 77 | } 78 | return "", false 79 | } 80 | -------------------------------------------------------------------------------- /internal/app/injector/auth.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 5 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth" 6 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth/jwtauth" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth/jwtauth/store/buntdb" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth/jwtauth/store/redis" 9 | jwt "github.com/dgrijalva/jwt-go" 10 | ) 11 | 12 | // InitAuth 初始化用户认证 13 | func InitAuth() (auth.Auther, func(), error) { 14 | cfg := config.C.JWTAuth 15 | 16 | var opts []jwtauth.Option 17 | opts = append(opts, jwtauth.SetExpired(cfg.Expired)) 18 | opts = append(opts, jwtauth.SetSigningKey([]byte(cfg.SigningKey))) 19 | opts = append(opts, jwtauth.SetKeyfunc(func(t *jwt.Token) (interface{}, error) { 20 | if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { 21 | return nil, auth.ErrInvalidToken 22 | } 23 | return []byte(cfg.SigningKey), nil 24 | })) 25 | 26 | var method jwt.SigningMethod 27 | switch cfg.SigningMethod { 28 | case "HS256": 29 | method = jwt.SigningMethodHS256 30 | case "HS384": 31 | method = jwt.SigningMethodHS384 32 | default: 33 | method = jwt.SigningMethodHS512 34 | } 35 | opts = append(opts, jwtauth.SetSigningMethod(method)) 36 | 37 | var store jwtauth.Storer 38 | switch cfg.Store { 39 | case "redis": 40 | rcfg := config.C.Redis 41 | store = redis.NewStore(&redis.Config{ 42 | Addr: rcfg.Addr, 43 | Password: rcfg.Password, 44 | DB: cfg.RedisDB, 45 | KeyPrefix: cfg.RedisPrefix, 46 | }) 47 | default: 48 | s, err := buntdb.NewStore(cfg.FilePath) 49 | if err != nil { 50 | return nil, nil, err 51 | } 52 | store = s 53 | } 54 | 55 | auth := jwtauth.New(store, opts...) 56 | cleanFunc := func() { 57 | auth.Release() 58 | } 59 | return auth, cleanFunc, nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/app/injector/casbin.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 7 | "github.com/casbin/casbin/v2" 8 | "github.com/casbin/casbin/v2/persist" 9 | ) 10 | 11 | // InitCasbin 初始化casbin 12 | func InitCasbin(adapter persist.Adapter) (*casbin.SyncedEnforcer, func(), error) { 13 | cfg := config.C.Casbin 14 | if cfg.Model == "" { 15 | return new(casbin.SyncedEnforcer), nil, nil 16 | } 17 | 18 | e, err := casbin.NewSyncedEnforcer(cfg.Model) 19 | if err != nil { 20 | return nil, nil, err 21 | } 22 | e.EnableLog(cfg.Debug) 23 | 24 | err = e.InitWithModelAndAdapter(e.GetModel(), adapter) 25 | if err != nil { 26 | return nil, nil, err 27 | } 28 | e.EnableEnforce(cfg.Enable) 29 | 30 | cleanFunc := func() {} 31 | if cfg.AutoLoad { 32 | e.StartAutoLoadPolicy(time.Duration(cfg.AutoLoadInternal) * time.Second) 33 | cleanFunc = func() { 34 | e.StopAutoLoadPolicy() 35 | } 36 | } 37 | 38 | return e, cleanFunc, nil 39 | } 40 | -------------------------------------------------------------------------------- /internal/app/injector/gorm.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "os" 9 | "path/filepath" 10 | "time" 11 | 12 | "github.com/jinzhu/gorm" 13 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 14 | igorm "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm" 15 | ) 16 | 17 | // InitGormDB 初始化gorm存储 18 | func InitGormDB() (*gorm.DB, func(), error) { 19 | cfg := config.C.Gorm 20 | db, cleanFunc, err := NewGormDB() 21 | if err != nil { 22 | return nil, cleanFunc, err 23 | } 24 | 25 | if cfg.EnableAutoMigrate { 26 | err = igorm.AutoMigrate(db) 27 | if err != nil { 28 | return nil, cleanFunc, err 29 | } 30 | } 31 | 32 | return db, cleanFunc, nil 33 | } 34 | 35 | func InitGormData(ctx context.Context, injector *Injector) error { 36 | // 初始化菜单数据 37 | if config.C.Menu.Enable && config.C.Menu.Data != "" { 38 | err := injector.MenuBll.InitData(ctx, config.C.Menu.Data) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | 44 | // 初始化页面管理数据 45 | if config.C.Page.Enable && config.C.Page.Data != "" { 46 | err := injector.PageBll.InitData(ctx, config.C.Page.Data) 47 | if err != nil { 48 | return err 49 | } 50 | } 51 | 52 | // 初始化模板商城数据 53 | if config.C.TplMall.Enable && config.C.TplMall.Data != "" { 54 | err := injector.GTplMallBll.InitData(ctx, config.C.TplMall.Data) 55 | if err != nil { 56 | return err 57 | } 58 | } 59 | 60 | //初始化平台数据 61 | err := injector.GPlatformBll.Create(ctx, schema.GPlatform{ 62 | AppID: ginplus.GetDefaultAppId(), 63 | Name: ginplus.GetDefaultAppName(), 64 | Status: 1, 65 | CreateTime: time.Now().Unix(), 66 | }) 67 | if err != nil { 68 | return err 69 | } 70 | return nil 71 | } 72 | 73 | // NewGormDB 创建DB实例 74 | func NewGormDB() (*gorm.DB, func(), error) { 75 | cfg := config.C 76 | var dsn string 77 | switch cfg.Gorm.DBType { 78 | case "mysql": 79 | dsn = cfg.MySQL.DSN() 80 | case "sqlite3": 81 | dsn = cfg.Sqlite3.DSN() 82 | _ = os.MkdirAll(filepath.Dir(dsn), 0777) 83 | case "postgres": 84 | dsn = cfg.Postgres.DSN() 85 | default: 86 | return nil, nil, errors.New("unknown db") 87 | } 88 | 89 | return igorm.NewDB(&igorm.Config{ 90 | Debug: cfg.Gorm.Debug, 91 | DBType: cfg.Gorm.DBType, 92 | DSN: dsn, 93 | MaxIdleConns: cfg.Gorm.MaxIdleConns, 94 | MaxLifetime: cfg.Gorm.MaxLifetime, 95 | MaxOpenConns: cfg.Gorm.MaxOpenConns, 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /internal/app/injector/injector.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "github.com/casbin/casbin/v2" 5 | "github.com/gin-gonic/gin" 6 | "github.com/google/wire" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth" 9 | ) 10 | 11 | // InjectorSet 注入Injector 12 | var InjectorSet = wire.NewSet(wire.Struct(new(Injector), "*")) 13 | 14 | // Injector 注入器(用于初始化完成之后的引用) 15 | type Injector struct { 16 | Engine *gin.Engine 17 | Auth auth.Auther 18 | CasbinEnforcer *casbin.SyncedEnforcer 19 | MenuBll bll.IMenu 20 | PageBll bll.IPageManager 21 | GPlatformBll bll.IGPlatform 22 | GTplMallBll bll.IGTplMall 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/injector/mongo.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 8 | imongo "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/mongo" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // InitMongo 初始化mongo存储 13 | func InitMongo() (*mongo.Client, func(), error) { 14 | cfg := config.C.Mongo 15 | client, cleanFunc, err := imongo.NewClient(&imongo.Config{ 16 | URI: cfg.URI, 17 | Database: cfg.Database, 18 | Timeout: time.Duration(cfg.Timeout) * time.Second, 19 | }) 20 | if err != nil { 21 | return nil, cleanFunc, err 22 | } 23 | 24 | err = imongo.CreateIndexes(context.Background(), client) 25 | if err != nil { 26 | return nil, cleanFunc, err 27 | } 28 | 29 | return client, cleanFunc, nil 30 | } 31 | -------------------------------------------------------------------------------- /internal/app/injector/web.go: -------------------------------------------------------------------------------- 1 | package injector 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/middleware" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/router" 7 | "github.com/LyricTian/gzip" 8 | "github.com/gin-gonic/gin" 9 | ginSwagger "github.com/swaggo/gin-swagger" 10 | swaggerFiles "github.com/swaggo/gin-swagger/swaggerFiles" 11 | ) 12 | 13 | // InitGinEngine 初始化gin引擎 14 | func InitGinEngine(r router.IRouter) *gin.Engine { 15 | gin.SetMode(config.C.RunMode) 16 | 17 | app := gin.New() 18 | app.NoMethod(middleware.NoMethodHandler()) 19 | app.NoRoute(middleware.NoRouteHandler()) 20 | 21 | prefixes := r.Prefixes() 22 | 23 | // Trace ID 24 | app.Use(middleware.TraceMiddleware(middleware.AllowPathPrefixNoSkipper(prefixes...))) 25 | 26 | // Copy body 27 | app.Use(middleware.CopyBodyMiddleware(middleware.AllowPathPrefixNoSkipper(prefixes...))) 28 | 29 | // Access logger 30 | app.Use(middleware.LoggerMiddleware(middleware.AllowPathPrefixNoSkipper(prefixes...))) 31 | 32 | // Recover 33 | app.Use(middleware.RecoveryMiddleware()) 34 | 35 | // CORS 36 | if config.C.CORS.Enable { 37 | app.Use(middleware.CORSMiddleware()) 38 | } 39 | 40 | // GZIP 41 | if config.C.GZIP.Enable { 42 | app.Use(gzip.Gzip(gzip.BestCompression, 43 | gzip.WithExcludedExtensions(config.C.GZIP.ExcludedExtentions), 44 | gzip.WithExcludedPaths(config.C.GZIP.ExcludedPaths), 45 | )) 46 | } 47 | 48 | // Router register 49 | r.Register(app) 50 | 51 | // Swagger 52 | if config.C.Swagger { 53 | app.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 54 | } 55 | 56 | // Website 57 | if dir := config.C.WWW; dir != "" { 58 | app.Use(middleware.WWWMiddleware(dir, middleware.AllowPathPrefixSkipper(prefixes...))) 59 | } 60 | 61 | return app 62 | } 63 | -------------------------------------------------------------------------------- /internal/app/injector/wire.go: -------------------------------------------------------------------------------- 1 | // +build wireinject 2 | // The build tag makes sure the stub is not built in the final build. 3 | 4 | package injector 5 | 6 | import ( 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/api" 8 | // "github.com/tanjiancheng/gin-amis-admin/internal/app/api/mock" 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/bll/impl/bll" 10 | "github.com/tanjiancheng/gin-amis-admin/internal/app/module/adapter" 11 | "github.com/tanjiancheng/gin-amis-admin/internal/app/router" 12 | "github.com/google/wire" 13 | 14 | // mongoModel "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/mongo/model" 15 | gormModel "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/model" 16 | ) 17 | 18 | // BuildInjector 生成注入器 19 | func BuildInjector() (*Injector, func(), error) { 20 | // 默认使用gorm存储注入,这里可使用 InitMongoDB & mongoModel.ModelSet 替换为 gorm 存储 21 | wire.Build( 22 | InitGormDB, 23 | gormModel.ModelSet, 24 | // InitMongoDB, 25 | // mongoModel.ModelSet, 26 | InitAuth, 27 | InitCasbin, 28 | InitGinEngine, 29 | bll.BllSet, 30 | api.APISet, 31 | // mock.MockSet, 32 | router.RouterSet, 33 | adapter.CasbinAdapterSet, 34 | InjectorSet, 35 | ) 36 | return new(Injector), nil, nil 37 | } 38 | -------------------------------------------------------------------------------- /internal/app/iutil/util.go: -------------------------------------------------------------------------------- 1 | package iutil 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 5 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 6 | "github.com/tanjiancheng/gin-amis-admin/pkg/trace" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/unique" 8 | ) 9 | 10 | var idFunc = func() string { 11 | return unique.NewSnowflakeID().String() 12 | } 13 | 14 | // InitID ... 15 | func InitID() { 16 | switch config.C.UniqueID.Type { 17 | case "uuid": 18 | idFunc = func() string { 19 | return unique.MustUUID().String() 20 | } 21 | case "object": 22 | idFunc = func() string { 23 | return unique.NewObjectID().Hex() 24 | } 25 | default: 26 | // Initialize snowflake node 27 | err := unique.SetSnowflakeNode(config.C.UniqueID.Snowflake.Node, config.C.UniqueID.Snowflake.Epoch) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | logger.SetTraceIDFunc(func() string { 33 | return unique.NewSnowflakeID().String() 34 | }) 35 | 36 | trace.SetIDFunc(func() string { 37 | return unique.NewSnowflakeID().String() 38 | }) 39 | 40 | idFunc = func() string { 41 | return unique.NewSnowflakeID().String() 42 | } 43 | } 44 | } 45 | 46 | // NewID Create unique id 47 | func NewID() string { 48 | return idFunc() 49 | } 50 | -------------------------------------------------------------------------------- /internal/app/logger.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 10 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 11 | loggerhook "github.com/tanjiancheng/gin-amis-admin/pkg/logger/hook" 12 | loggergormhook "github.com/tanjiancheng/gin-amis-admin/pkg/logger/hook/gorm" 13 | loggermongohook "github.com/tanjiancheng/gin-amis-admin/pkg/logger/hook/mongo" 14 | "github.com/sirupsen/logrus" 15 | ) 16 | 17 | // InitLogger 初始化日志模块 18 | func InitLogger() (func(), error) { 19 | c := config.C.Log 20 | logger.SetLevel(c.Level) 21 | logger.SetFormatter(c.Format) 22 | 23 | // 设定日志输出 24 | var file *os.File 25 | if c.Output != "" { 26 | switch c.Output { 27 | case "stdout": 28 | logger.SetOutput(os.Stdout) 29 | case "stderr": 30 | logger.SetOutput(os.Stderr) 31 | case "file": 32 | if name := c.OutputFile; name != "" { 33 | _ = os.MkdirAll(filepath.Dir(name), 0777) 34 | 35 | f, err := os.OpenFile(name, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) 36 | if err != nil { 37 | return nil, err 38 | } 39 | logger.SetOutput(f) 40 | file = f 41 | } 42 | } 43 | } 44 | 45 | var hook *loggerhook.Hook 46 | if c.EnableHook { 47 | var hookLevels []logrus.Level 48 | for _, lvl := range c.HookLevels { 49 | plvl, err := logrus.ParseLevel(lvl) 50 | if err != nil { 51 | return nil, err 52 | } 53 | hookLevels = append(hookLevels, plvl) 54 | } 55 | 56 | switch { 57 | case c.Hook.IsGorm(): 58 | hc := config.C.LogGormHook 59 | 60 | var dsn string 61 | switch hc.DBType { 62 | case "mysql": 63 | dsn = config.C.MySQL.DSN() 64 | case "sqlite3": 65 | dsn = config.C.Sqlite3.DSN() 66 | case "postgres": 67 | dsn = config.C.Postgres.DSN() 68 | default: 69 | return nil, errors.New("unknown db") 70 | } 71 | 72 | h := loggerhook.New(loggergormhook.New(&loggergormhook.Config{ 73 | DBType: hc.DBType, 74 | DSN: dsn, 75 | MaxLifetime: hc.MaxLifetime, 76 | MaxOpenConns: hc.MaxOpenConns, 77 | MaxIdleConns: hc.MaxIdleConns, 78 | TableName: hc.Table, 79 | }), 80 | loggerhook.SetMaxWorkers(c.HookMaxThread), 81 | loggerhook.SetMaxQueues(c.HookMaxBuffer), 82 | loggerhook.SetLevels(hookLevels...), 83 | ) 84 | logger.AddHook(h) 85 | hook = h 86 | case c.Hook.IsMongo(): 87 | h := loggerhook.New(loggermongohook.New(&loggermongohook.Config{ 88 | URI: config.C.Mongo.URI, 89 | Database: config.C.Mongo.Database, 90 | Timeout: time.Duration(config.C.Mongo.Timeout) * time.Second, 91 | Collection: config.C.LogMongoHook.Collection, 92 | }), 93 | loggerhook.SetMaxWorkers(c.HookMaxThread), 94 | loggerhook.SetMaxQueues(c.HookMaxBuffer), 95 | loggerhook.SetLevels(hookLevels...), 96 | ) 97 | logger.AddHook(h) 98 | hook = h 99 | } 100 | } 101 | 102 | return func() { 103 | if file != nil { 104 | file.Close() 105 | } 106 | 107 | if hook != nil { 108 | hook.Flush() 109 | } 110 | }, nil 111 | } 112 | -------------------------------------------------------------------------------- /internal/app/middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // NoMethodHandler 未找到请求方法的处理函数 13 | func NoMethodHandler() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | ginplus.ResError(c, errors.ErrMethodNotAllow) 16 | } 17 | } 18 | 19 | // NoRouteHandler 未找到请求路由的处理函数 20 | func NoRouteHandler() gin.HandlerFunc { 21 | return func(c *gin.Context) { 22 | ginplus.ResError(c, errors.ErrNotFound) 23 | } 24 | } 25 | 26 | // SkipperFunc 定义中间件跳过函数 27 | type SkipperFunc func(*gin.Context) bool 28 | 29 | // AllowPathPrefixSkipper 检查请求路径是否包含指定的前缀,如果包含则跳过 30 | func AllowPathPrefixSkipper(prefixes ...string) SkipperFunc { 31 | return func(c *gin.Context) bool { 32 | path := c.Request.URL.Path 33 | pathLen := len(path) 34 | 35 | for _, p := range prefixes { 36 | if pl := len(p); pathLen >= pl && path[:pl] == p { 37 | return true 38 | } 39 | } 40 | return false 41 | } 42 | } 43 | 44 | // AllowPathPrefixNoSkipper 检查请求路径是否包含指定的前缀,如果包含则不跳过 45 | func AllowPathPrefixNoSkipper(prefixes ...string) SkipperFunc { 46 | return func(c *gin.Context) bool { 47 | path := c.Request.URL.Path 48 | pathLen := len(path) 49 | 50 | for _, p := range prefixes { 51 | if pl := len(p); pathLen >= pl && path[:pl] == p { 52 | return false 53 | } 54 | } 55 | return true 56 | } 57 | } 58 | 59 | // AllowMethodAndPathPrefixSkipper 检查请求方法和路径是否包含指定的前缀,如果不包含则跳过 60 | func AllowMethodAndPathPrefixSkipper(prefixes ...string) SkipperFunc { 61 | return func(c *gin.Context) bool { 62 | path := JoinRouter(c.Request.Method, c.Request.URL.Path) 63 | pathLen := len(path) 64 | 65 | for _, p := range prefixes { 66 | if pl := len(p); pathLen >= pl && path[:pl] == p { 67 | return true 68 | } 69 | } 70 | return false 71 | } 72 | } 73 | 74 | // JoinRouter 拼接路由 75 | func JoinRouter(method, path string) string { 76 | if len(path) > 0 && path[0] != '/' { 77 | path = "/" + path 78 | } 79 | return fmt.Sprintf("%s%s", strings.ToUpper(method), path) 80 | } 81 | 82 | // SkipHandler 统一处理跳过函数 83 | func SkipHandler(c *gin.Context, skippers ...SkipperFunc) bool { 84 | for _, skipper := range skippers { 85 | if skipper(c) { 86 | return true 87 | } 88 | } 89 | return false 90 | } 91 | 92 | // EmptyMiddleware 不执行业务处理的中间件 93 | func EmptyMiddleware() gin.HandlerFunc { 94 | return func(c *gin.Context) { 95 | c.Next() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_app_id.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/jinzhu/gorm" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 8 | "strings" 9 | ) 10 | 11 | // AppIdMiddleware 应用ID中间件,区分访问不同的应用请求 12 | func AppIdMiddleware(skippers ...SkipperFunc) gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | if SkipHandler(c, skippers...) { 15 | c.Next() 16 | return 17 | } 18 | 19 | appID := ginplus.GetScopeAppId(c) 20 | ginplus.SetAppID(c, appID) 21 | c.Writer.Header().Set("X-App-Id", appID) 22 | ginplus.SetTablePrefix(appID) //切换表前缀 23 | 24 | gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultName string) string { 25 | if len(defaultName) <= 0 { 26 | return defaultName 27 | } 28 | path := strings.Split(defaultName, "_") 29 | prefix := path[0] 30 | if prefix+"_" == config.C.Gorm.GlobalTablePrefix { //全局表不需要切换 31 | return defaultName 32 | } 33 | oldPath := prefix + "_" + path[1] 34 | newPath := prefix + "_" + appID 35 | return strings.Replace(defaultName, oldPath, newPath, -1) 36 | } 37 | c.Next() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/icontext" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth" 10 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 11 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | func wrapUserAuthContext(c *gin.Context, userID string) { 16 | ginplus.SetUserID(c, userID) 17 | ctx := icontext.NewUserID(c.Request.Context(), userID) 18 | ctx = logger.NewUserIDContext(ctx, userID) 19 | c.Request = c.Request.WithContext(ctx) 20 | } 21 | 22 | // UserAuthMiddleware 用户授权中间件 23 | func UserAuthMiddleware(a auth.Auther, skippers ...SkipperFunc) gin.HandlerFunc { 24 | if !config.C.JWTAuth.Enable { 25 | return func(c *gin.Context) { 26 | wrapUserAuthContext(c, config.C.Root.UserName) 27 | c.Next() 28 | } 29 | } 30 | 31 | return func(c *gin.Context) { 32 | if SkipHandler(c, skippers...) { 33 | c.Next() 34 | return 35 | } 36 | 37 | subjectJsonStr, err := a.ParseUserID(c.Request.Context(), ginplus.GetToken(c)) 38 | var subjectInfo schema.SubjectInfo 39 | jsonErr := json.Unmarshal([]byte(subjectJsonStr), &subjectInfo) 40 | userID := subjectInfo.UserID 41 | appID := subjectInfo.AppID 42 | 43 | ginplus.SetAppID(c, appID) 44 | if err != nil || jsonErr != nil { 45 | if err == auth.ErrInvalidToken { 46 | if config.C.IsDebugMode() { 47 | wrapUserAuthContext(c, config.C.Root.UserName) 48 | c.Next() 49 | return 50 | } 51 | ginplus.ResError(c, errors.ErrInvalidToken) 52 | return 53 | } 54 | ginplus.ResError(c, errors.WithStack(err)) 55 | return 56 | } 57 | 58 | wrapUserAuthContext(c, userID) 59 | c.Next() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_casbin.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/casbin/casbin/v2" 5 | "github.com/gin-gonic/gin" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 9 | ) 10 | 11 | // CasbinMiddleware casbin中间件 12 | func CasbinMiddleware(enforcer *casbin.SyncedEnforcer, skippers ...SkipperFunc) gin.HandlerFunc { 13 | cfg := config.C.Casbin 14 | if !cfg.Enable { 15 | return EmptyMiddleware() 16 | } 17 | 18 | return func(c *gin.Context) { 19 | if SkipHandler(c, skippers...) { 20 | c.Next() 21 | return 22 | } 23 | 24 | p := c.Request.URL.Path 25 | m := c.Request.Method 26 | if b, err := enforcer.Enforce(ginplus.GetUserID(c), p, m); err != nil { 27 | ginplus.ResError(c, errors.WithStack(err)) 28 | return 29 | } else if !b { 30 | ginplus.ResError(c, errors.ErrNoPerm) 31 | return 32 | } 33 | c.Next() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_copy_body.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "io" 7 | "io/ioutil" 8 | "net/http" 9 | 10 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 11 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | // CopyBodyMiddleware Copy body 16 | func CopyBodyMiddleware(skippers ...SkipperFunc) gin.HandlerFunc { 17 | var maxMemory int64 = 64 << 20 // 64 MB 18 | if v := config.C.HTTP.MaxContentLength; v > 0 { 19 | maxMemory = v 20 | } 21 | 22 | return func(c *gin.Context) { 23 | if SkipHandler(c, skippers...) || c.Request.Body == nil { 24 | c.Next() 25 | return 26 | } 27 | 28 | var requestBody []byte 29 | isGzip := false 30 | safe := &io.LimitedReader{R: c.Request.Body, N: maxMemory} 31 | 32 | if c.GetHeader("Content-Encoding") == "gzip" { 33 | reader, err := gzip.NewReader(safe) 34 | if err == nil { 35 | isGzip = true 36 | requestBody, _ = ioutil.ReadAll(reader) 37 | } 38 | } 39 | 40 | if !isGzip { 41 | requestBody, _ = ioutil.ReadAll(safe) 42 | } 43 | 44 | c.Request.Body.Close() 45 | bf := bytes.NewBuffer(requestBody) 46 | c.Request.Body = http.MaxBytesReader(c.Writer, ioutil.NopCloser(bf), maxMemory) 47 | c.Set(ginplus.ReqBodyKey, requestBody) 48 | 49 | c.Next() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 7 | "github.com/gin-contrib/cors" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // CORSMiddleware 跨域请求中间件 12 | func CORSMiddleware() gin.HandlerFunc { 13 | cfg := config.C.CORS 14 | return cors.New(cors.Config{ 15 | AllowOrigins: cfg.AllowOrigins, 16 | AllowMethods: cfg.AllowMethods, 17 | AllowHeaders: cfg.AllowHeaders, 18 | AllowCredentials: cfg.AllowCredentials, 19 | MaxAge: time.Second * time.Duration(cfg.MaxAge), 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "mime" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | // LoggerMiddleware 日志中间件 14 | func LoggerMiddleware(skippers ...SkipperFunc) gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | if SkipHandler(c, skippers...) { 17 | c.Next() 18 | return 19 | } 20 | 21 | p := c.Request.URL.Path 22 | method := c.Request.Method 23 | span := logger.StartSpan(c.Request.Context(), 24 | logger.SetSpanTitle("Request"), 25 | logger.SetSpanFuncName(JoinRouter(method, p))) 26 | 27 | start := time.Now() 28 | 29 | fields := make(map[string]interface{}) 30 | fields["ip"] = c.ClientIP() 31 | fields["method"] = method 32 | fields["url"] = c.Request.URL.String() 33 | fields["proto"] = c.Request.Proto 34 | fields["header"] = c.Request.Header 35 | fields["user_agent"] = c.GetHeader("User-Agent") 36 | fields["content_length"] = c.Request.ContentLength 37 | 38 | if method == http.MethodPost || method == http.MethodPut { 39 | mediaType, _, _ := mime.ParseMediaType(c.GetHeader("Content-Type")) 40 | if mediaType != "multipart/form-data" { 41 | if v, ok := c.Get(ginplus.ReqBodyKey); ok { 42 | if b, ok := v.([]byte); ok { 43 | fields["body"] = string(b) 44 | } 45 | } 46 | } 47 | } 48 | c.Next() 49 | 50 | timeConsuming := time.Since(start).Nanoseconds() / 1e6 51 | fields["res_status"] = c.Writer.Status() 52 | fields["res_length"] = c.Writer.Size() 53 | 54 | if v, ok := c.Get(ginplus.LoggerReqBodyKey); ok { 55 | if b, ok := v.([]byte); ok { 56 | fields["body"] = string(b) 57 | } 58 | } 59 | 60 | if v, ok := c.Get(ginplus.ResBodyKey); ok { 61 | if b, ok := v.([]byte); ok { 62 | fields["res_body"] = string(b) 63 | } 64 | } 65 | 66 | fields[logger.UserIDKey] = ginplus.GetUserID(c) 67 | span.WithFields(fields).Infof("[http] %s-%s-%s-%d(%dms)", 68 | p, c.Request.Method, c.ClientIP(), c.Writer.Status(), timeConsuming) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_rate_limiter.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 10 | "github.com/gin-gonic/gin" 11 | "github.com/go-redis/redis" 12 | "github.com/go-redis/redis_rate" 13 | "golang.org/x/time/rate" 14 | ) 15 | 16 | // RateLimiterMiddleware 请求频率限制中间件 17 | func RateLimiterMiddleware(skippers ...SkipperFunc) gin.HandlerFunc { 18 | cfg := config.C.RateLimiter 19 | if !cfg.Enable { 20 | return EmptyMiddleware() 21 | } 22 | 23 | rc := config.C.Redis 24 | ring := redis.NewRing(&redis.RingOptions{ 25 | Addrs: map[string]string{ 26 | "server1": rc.Addr, 27 | }, 28 | Password: rc.Password, 29 | DB: cfg.RedisDB, 30 | }) 31 | 32 | limiter := redis_rate.NewLimiter(ring) 33 | limiter.Fallback = rate.NewLimiter(rate.Inf, 0) 34 | 35 | return func(c *gin.Context) { 36 | if SkipHandler(c, skippers...) { 37 | c.Next() 38 | return 39 | } 40 | 41 | userID := ginplus.GetUserID(c) 42 | if userID != "" { 43 | limit := cfg.Count 44 | rate, delay, allowed := limiter.AllowMinute(userID, limit) 45 | if !allowed { 46 | h := c.Writer.Header() 47 | h.Set("X-RateLimit-Limit", strconv.FormatInt(limit, 10)) 48 | h.Set("X-RateLimit-Remaining", strconv.FormatInt(limit-rate, 10)) 49 | delaySec := int64(delay / time.Second) 50 | h.Set("X-RateLimit-Delay", strconv.FormatInt(delaySec, 10)) 51 | ginplus.ResError(c, errors.ErrTooManyRequests) 52 | return 53 | } 54 | } 55 | 56 | c.Next() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_recover.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "runtime" 8 | 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 10 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 11 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | var ( 16 | dunno = []byte("???") 17 | centerDot = []byte("·") 18 | dot = []byte(".") 19 | slash = []byte("/") 20 | ) 21 | 22 | // RecoveryMiddleware 崩溃恢复中间件 23 | func RecoveryMiddleware() gin.HandlerFunc { 24 | return func(c *gin.Context) { 25 | defer func() { 26 | if err := recover(); err != nil { 27 | stack := stack(3) 28 | logger.StartSpan(c.Request.Context()).WithField("stack", string(stack)).Errorf("[panic]: %v", err) 29 | ginplus.ResError(c, errors.ErrInternalServer) 30 | } 31 | }() 32 | c.Next() 33 | } 34 | } 35 | 36 | // stack returns a nicely formatted stack frame, skipping skip frames. 37 | func stack(skip int) []byte { 38 | buf := new(bytes.Buffer) // the returned data 39 | // As we loop, we open files and read them. These variables record the currently 40 | // loaded file. 41 | var lines [][]byte 42 | var lastFile string 43 | for i := skip; ; i++ { // Skip the expected number of frames 44 | pc, file, line, ok := runtime.Caller(i) 45 | if !ok { 46 | break 47 | } 48 | // Print this much at least. If we can't find the source, it won't show. 49 | fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 50 | if file != lastFile { 51 | data, err := ioutil.ReadFile(file) 52 | if err != nil { 53 | continue 54 | } 55 | lines = bytes.Split(data, []byte{'\n'}) 56 | lastFile = file 57 | } 58 | fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 59 | } 60 | return buf.Bytes() 61 | } 62 | 63 | // source returns a space-trimmed slice of the n'th line. 64 | func source(lines [][]byte, n int) []byte { 65 | n-- // in stack trace, lines are 1-indexed but our array is 0-indexed 66 | if n < 0 || n >= len(lines) { 67 | return dunno 68 | } 69 | return bytes.TrimSpace(lines[n]) 70 | } 71 | 72 | // function returns, if possible, the name of the function containing the PC. 73 | func function(pc uintptr) []byte { 74 | fn := runtime.FuncForPC(pc) 75 | if fn == nil { 76 | return dunno 77 | } 78 | name := []byte(fn.Name()) 79 | // The name includes the path name to the package, which is unnecessary 80 | // since the file name is already included. Plus, it has center dots. 81 | // That is, we see 82 | // runtime/debug.*T·ptrmethod 83 | // and want 84 | // *T.ptrmethod 85 | // Also the package path might contains dot (e.g. code.google.com/...), 86 | // so first eliminate the path prefix 87 | if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { 88 | name = name[lastslash+1:] 89 | } 90 | if period := bytes.Index(name, dot); period >= 0 { 91 | name = name[period+1:] 92 | } 93 | name = bytes.Replace(name, centerDot, dot, -1) 94 | return name 95 | } 96 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_trace.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/internal/app/icontext" 5 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 6 | "github.com/tanjiancheng/gin-amis-admin/pkg/trace" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // TraceMiddleware 跟踪ID中间件 11 | func TraceMiddleware(skippers ...SkipperFunc) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | if SkipHandler(c, skippers...) { 14 | c.Next() 15 | return 16 | } 17 | 18 | // 优先从请求头中获取请求ID 19 | traceID := c.GetHeader("X-Request-Id") 20 | if traceID == "" { 21 | traceID = trace.NewID() 22 | } 23 | 24 | ctx := icontext.NewTraceID(c.Request.Context(), traceID) 25 | ctx = logger.NewTraceIDContext(ctx, traceID) 26 | c.Request = c.Request.WithContext(ctx) 27 | c.Writer.Header().Set("X-Trace-Id", traceID) 28 | 29 | c.Next() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /internal/app/middleware/mw_www.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // WWWMiddleware 静态站点中间件 11 | func WWWMiddleware(root string, skippers ...SkipperFunc) gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | if SkipHandler(c, skippers...) { 14 | c.Next() 15 | return 16 | } 17 | 18 | p := c.Request.URL.Path 19 | fpath := filepath.Join(root, filepath.FromSlash(p)) 20 | _, err := os.Stat(fpath) 21 | if err != nil && os.IsNotExist(err) { 22 | fpath = filepath.Join(root, "index.html") 23 | } 24 | 25 | c.File(fpath) 26 | c.Abort() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_demo.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetDemoDB 获取demo存储 12 | func GetDemoDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(Demo)) 14 | } 15 | 16 | // SchemaDemo demo对象 17 | type SchemaDemo schema.Demo 18 | 19 | // ToDemo 转换为demo实体 20 | func (a SchemaDemo) ToDemo() *Demo { 21 | item := new(Demo) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // Demo demo实体 27 | type Demo struct { 28 | Model 29 | Code string `gorm:"column:code;size:50;index;default:'';not null;"` // 编号 30 | Name string `gorm:"column:name;size:100;index;default:'';not null;"` // 名称 31 | Memo *string `gorm:"column:memo;size:200;"` // 备注 32 | Status int `gorm:"column:status;index;default:0;not null;"` // 状态(1:启用 2:停用) 33 | Creator string `gorm:"column:creator;size:36;"` // 创建者 34 | } 35 | 36 | // TableName 表名 37 | func (a Demo) TableName() string { 38 | return a.Model.TableName("demo") 39 | } 40 | 41 | // ToSchemaDemo 转换为demo对象 42 | func (a Demo) ToSchemaDemo() *schema.Demo { 43 | item := new(schema.Demo) 44 | util.StructMapToStruct(a, item) 45 | return item 46 | } 47 | 48 | // Demos demo列表 49 | type Demos []*Demo 50 | 51 | // ToSchemaDemos 转换为demo对象列表 52 | func (a Demos) ToSchemaDemos() []*schema.Demo { 53 | list := make([]*schema.Demo, len(a)) 54 | for i, item := range a { 55 | list[i] = item.ToSchemaDemo() 56 | } 57 | return list 58 | } 59 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_g_platform.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/jinzhu/gorm" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 10 | ) 11 | 12 | type GPlatform struct { 13 | ID int `gorm:"column:id;primary_key;auto_increment;"` 14 | AppID string `gorm:"column:app_id;unique_index;size:36;not null;"` // 平台ID 15 | Name string `gorm:"column:name;size:100;index;default:'';not null;"` // 平台名称 16 | Status int `gorm:"column:status;default:0;not null;"` // 状态(1:启用 -1:停用) 17 | CreateTime int64 `gorm:"column:create_time;"` // 创建时间 18 | } 19 | 20 | func (a GPlatform) ToSchemaGPlatform() *schema.GPlatform { 21 | item := new(schema.GPlatform) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // TableName 表名 27 | func (a GPlatform) TableName() string { 28 | return fmt.Sprintf("%s%s", config.C.Gorm.GlobalTablePrefix, "platform") 29 | } 30 | 31 | type SchemaGPlatform schema.GPlatform 32 | 33 | func (a SchemaGPlatform) ToGPlatform() *GPlatform { 34 | item := new(GPlatform) 35 | util.StructMapToStruct(a, item) 36 | return item 37 | } 38 | 39 | func GetGPlatformDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 40 | return GetDBWithModel(ctx, defDB, new(GPlatform)) 41 | } 42 | 43 | type GPlatforms []*GPlatform 44 | 45 | func (a GPlatforms) ToSchemaGPlatforms() []*schema.GPlatform { 46 | list := make([]*schema.GPlatform, len(a)) 47 | for i, item := range a { 48 | list[i] = item.ToSchemaGPlatform() 49 | } 50 | return list 51 | } 52 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_g_tpl_mall.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/jinzhu/gorm" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 10 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 11 | ) 12 | 13 | type GTplMall struct { 14 | ID int `gorm:"column:id;primary_key;auto_increment;"` // 自增ID 15 | Identify string `gorm:"unique_index;column:identify;size:64;default:'';not null;"` // 模板标识 16 | Scope string `gorm:"column:scope;size:256;index;not null;"` // 应用限制 *为所有使用,其他情况为具体的app_id下才能可见 17 | Name string `gorm:"column:name;size:128;index;default:'';not null;"` // 模板名称 18 | Desc string `gorm:"column:desc;size:256;default:'';not null;"` // 模板说明 19 | Meta string `gorm:"column:meta;type:longtext;not null;"` // 页面元信息 20 | Source string `gorm:"column:source;type:longtext;not null;"` // 页面源码 21 | MockData string `gorm:"column:mock_data;type:longtext;not null;"` // mock接口数据 22 | Icon string `gorm:"column:icon;size:32;default:'';not null;"` // 模板图标 23 | Status int `gorm:"column:status;default:0;not null;"` // 模板状态 0未发布 1正常 -1禁用 24 | Creator string `gorm:"column:creator;size:32;default:'';not null;"` // 创建者 25 | CreateTime int64 `gorm:"column:create_time;"` // 创建时间 26 | UpdateTime int64 `gorm:"column:update_time;"` // 更新时间 27 | } 28 | 29 | func (a GTplMall) ToSchemaGTplMall() *schema.GTplMall { 30 | mockData := a.MockData 31 | item := new(schema.GTplMall) 32 | _ = json.Unmarshal([]byte(mockData), &item.MockData) 33 | util.StructMapToStruct(a, item) 34 | return item 35 | } 36 | 37 | // TableName 表名 38 | func (a GTplMall) TableName() string { 39 | return fmt.Sprintf("%s%s", config.C.Gorm.GlobalTablePrefix, "tpl_mall") 40 | } 41 | 42 | type SchemaGTplMall schema.GTplMall 43 | 44 | func (a SchemaGTplMall) ToGTplMall() *GTplMall { 45 | mockData := a.MockData 46 | mockDataJsonStr, _ := json.Marshal(mockData) 47 | item := new(GTplMall) 48 | item.MockData = string(mockDataJsonStr) 49 | util.StructMapToStruct(a, item) 50 | return item 51 | } 52 | 53 | func GetGTplMallDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 54 | return GetDBWithModel(ctx, defDB, new(GTplMall)) 55 | } 56 | 57 | type GTplMalls []*GTplMall 58 | 59 | func (a GTplMalls) ToSchemaGTplMalls() []*schema.GTplMall { 60 | list := make([]*schema.GTplMall, len(a)) 61 | for i, item := range a { 62 | list[i] = item.ToSchemaGTplMall() 63 | } 64 | return list 65 | } 66 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_menu.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetMenuDB 获取菜单存储 12 | func GetMenuDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(Menu)) 14 | } 15 | 16 | // SchemaMenu 菜单对象 17 | type SchemaMenu schema.Menu 18 | 19 | // ToMenu 转换为菜单实体 20 | func (a SchemaMenu) ToMenu() *Menu { 21 | item := new(Menu) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // Menu 菜单实体 27 | type Menu struct { 28 | Model 29 | Name string `gorm:"column:name;size:50;index;default:'';not null;"` // 菜单名称 30 | Sequence int `gorm:"column:sequence;index;default:0;not null;"` // 排序值 31 | Icon *string `gorm:"column:icon;size:255;"` // 菜单图标 32 | Router *string `gorm:"column:router;size:255;"` // 访问路由 33 | ParentID *string `gorm:"column:parent_id;size:36;index;"` // 父级内码 34 | ParentPath *string `gorm:"column:parent_path;size:518;index;"` // 父级路径 35 | ShowStatus int `gorm:"column:show_status;index;default:0;not null;"` // 状态(1:显示 2:隐藏) 36 | Status int `gorm:"column:status;index;default:0;not null;"` // 状态(1:启用 2:禁用) 37 | Memo *string `gorm:"column:memo;size:1024;"` // 备注 38 | Creator string `gorm:"column:creator;size:36;"` // 创建人 39 | } 40 | 41 | // TableName 表名 42 | func (a Menu) TableName() string { 43 | return a.Model.TableName("menu") 44 | } 45 | 46 | // ToSchemaMenu 转换为菜单对象 47 | func (a Menu) ToSchemaMenu() *schema.Menu { 48 | item := new(schema.Menu) 49 | util.StructMapToStruct(a, item) 50 | return item 51 | } 52 | 53 | // Menus 菜单实体列表 54 | type Menus []*Menu 55 | 56 | // ToSchemaMenus 转换为菜单对象列表 57 | func (a Menus) ToSchemaMenus() []*schema.Menu { 58 | list := make([]*schema.Menu, len(a)) 59 | for i, item := range a { 60 | list[i] = item.ToSchemaMenu() 61 | } 62 | return list 63 | } 64 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_menu_action.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetMenuActionDB 菜单动作 12 | func GetMenuActionDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(MenuAction)) 14 | } 15 | 16 | // SchemaMenuAction 菜单动作 17 | type SchemaMenuAction schema.MenuAction 18 | 19 | // ToMenuAction 转换为菜单动作实体 20 | func (a SchemaMenuAction) ToMenuAction() *MenuAction { 21 | item := new(MenuAction) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // MenuAction 菜单动作实体 27 | type MenuAction struct { 28 | Model 29 | MenuID string `gorm:"column:menu_id;size:36;index;default:'';not null;"` // 菜单ID 30 | Code string `gorm:"column:code;size:100;default:'';not null;"` // 动作编号 31 | Name string `gorm:"column:name;size:100;default:'';not null;"` // 动作名称 32 | } 33 | 34 | // TableName 表名 35 | func (a MenuAction) TableName() string { 36 | return a.Model.TableName("menu_action") 37 | } 38 | 39 | // ToSchemaMenuAction 转换为菜单动作对象 40 | func (a MenuAction) ToSchemaMenuAction() *schema.MenuAction { 41 | item := new(schema.MenuAction) 42 | util.StructMapToStruct(a, item) 43 | return item 44 | } 45 | 46 | // MenuActions 菜单动作列表 47 | type MenuActions []*MenuAction 48 | 49 | // ToSchemaMenuActions 转换为菜单动作对象列表 50 | func (a MenuActions) ToSchemaMenuActions() []*schema.MenuAction { 51 | list := make([]*schema.MenuAction, len(a)) 52 | for i, item := range a { 53 | list[i] = item.ToSchemaMenuAction() 54 | } 55 | return list 56 | } 57 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_menu_action_resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetMenuActionResourceDB 菜单动作关联资源 12 | func GetMenuActionResourceDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(MenuActionResource)) 14 | } 15 | 16 | // SchemaMenuActionResource 菜单动作关联资源 17 | type SchemaMenuActionResource schema.MenuActionResource 18 | 19 | // ToMenuActionResource 转换为菜单动作关联资源实体 20 | func (a SchemaMenuActionResource) ToMenuActionResource() *MenuActionResource { 21 | item := new(MenuActionResource) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // MenuActionResource 菜单动作关联资源实体 27 | type MenuActionResource struct { 28 | Model 29 | ActionID string `gorm:"column:action_id;size:36;index;default:'';not null;"` // 菜单动作ID 30 | Method string `gorm:"column:method;size:100;default:'';not null;"` // 资源请求方式(支持正则) 31 | Path string `gorm:"column:path;size:100;default:'';not null;"` // 资源请求路径(支持/:id匹配) 32 | } 33 | 34 | // TableName 表名 35 | func (a MenuActionResource) TableName() string { 36 | return a.Model.TableName("menu_action_resource") 37 | } 38 | 39 | // ToSchemaMenuActionResource 转换为菜单动作关联资源对象 40 | func (a MenuActionResource) ToSchemaMenuActionResource() *schema.MenuActionResource { 41 | item := new(schema.MenuActionResource) 42 | util.StructMapToStruct(a, item) 43 | return item 44 | } 45 | 46 | // MenuActionResources 菜单动作关联资源列表 47 | type MenuActionResources []*MenuActionResource 48 | 49 | // ToSchemaMenuActionResources 转换为菜单动作关联资源对象列表 50 | func (a MenuActionResources) ToSchemaMenuActionResources() []*schema.MenuActionResource { 51 | list := make([]*schema.MenuActionResource, len(a)) 52 | for i, item := range a { 53 | list[i] = item.ToSchemaMenuActionResource() 54 | } 55 | return list 56 | } 57 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_page_manager.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "github.com/jinzhu/gorm" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/iutil" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 9 | ) 10 | 11 | // GetPageManagerDb 获取页面管理存储 12 | func GetPageManagerDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(PageManager)) 14 | } 15 | 16 | // SchemaPageManager 页面管理对象 17 | type SchemaPageManager schema.PageManager 18 | 19 | // ToPageManager 转换为页面管理实体 20 | func (a SchemaPageManager) ToPageManager() *PageManager { 21 | item := new(PageManager) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // PageManager 页面管理实体 27 | type PageManager struct { 28 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT"` // 自增ID 29 | Identify string `gorm:"unique_index;column:identify;size:64;default:'';not null;"` // 页面标识 30 | Name string `gorm:"column:name;size:64;default:'';not null;"` // 页面名称 31 | Meta string `gorm:"column:meta;type:longtext;not null;"` // 页面元信息 32 | Source string `gorm:"column:source;type:longtext;not null;"` // 页面源码 33 | Creator string `gorm:"column:creator;size:32;default:'';not null;"` // 创建者 34 | CreateTime int64 `gorm:"column:create_time;"` // 创建时间 35 | ModifyTime int64 `gorm:"column:modify_time;"` // 更新时间 36 | } 37 | 38 | // TableName 表名 39 | func (a PageManager) TableName() string { 40 | return Model{}.TableName("page_manager") 41 | } 42 | 43 | // ToSchemaPageManager 转换为页面管理对象 44 | func (a PageManager) ToSchemaPageManager() *schema.PageManager { 45 | meta := a.Meta 46 | source := a.Source 47 | jsonPathx := new(iutil.JsonPathx) 48 | item := new(schema.PageManager) 49 | item.RenderSource, _ = jsonPathx.Search(meta, source) 50 | util.StructMapToStruct(a, item) 51 | return item 52 | } 53 | 54 | type PageManagers []*PageManager 55 | 56 | // ToSchemaPageManagers 转换为页面管理对象列表 57 | func (a PageManagers) ToSchemaPageManagers() []*schema.PageManager { 58 | list := make([]*schema.PageManager, len(a)) 59 | for i, item := range a { 60 | list[i] = item.ToSchemaPageManager() 61 | } 62 | return list 63 | } 64 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_page_version_history.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 6 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | // GetPageVersionHistory 获取页面版本存储 11 | func GetPageVersionHistoryDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 12 | return GetDBWithModel(ctx, defDB, new(PageVersionHistory)) 13 | } 14 | 15 | // SchemaPageVersionHistory 页面版本对象 16 | type SchemaPageVersionHistory schema.PageVersionHistory 17 | 18 | // ToPageVersionHistory 转换为页面版本实体 19 | func (a SchemaPageVersionHistory) ToPageVersionHistory() *PageVersionHistory { 20 | item := new(PageVersionHistory) 21 | util.StructMapToStruct(a, item) 22 | return item 23 | } 24 | 25 | // PageVersionHistory 页面版本实体 26 | type PageVersionHistory struct { 27 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT"` // 自增ID 28 | PageManagerId int `gorm:"column:page_manager_id;default:0;not null;"` // 页面标识 29 | PageManagerInfo string `gorm:"column:page_manger_info;type:longtext;not null;"` // 页面源码 30 | CreateTime int64 `gorm:"column:create_time;"` // 创建时间 31 | } 32 | 33 | // TableName 表名 34 | func (a PageVersionHistory) TableName() string { 35 | return Model{}.TableName("page_version_history") 36 | } 37 | 38 | // ToSchemaPageVersionHistory 转换为页面版本对象 39 | func (a PageVersionHistory) ToSchemaPageVersionHistory() *schema.PageVersionHistory { 40 | item := new(schema.PageVersionHistory) 41 | util.StructMapToStruct(a, item) 42 | return item 43 | } 44 | 45 | type PageVersionHistorys []*PageVersionHistory 46 | 47 | // ToSchemaPageVersionHistorys 转换为页面版本对象列表 48 | func (a PageVersionHistorys) ToSchemaPageVersionHistorys() []*schema.PageVersionHistory { 49 | list := make([]*schema.PageVersionHistory, len(a)) 50 | for i, item := range a { 51 | list[i] = item.ToSchemaPageVersionHistory() 52 | } 53 | return list 54 | } 55 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_role.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetRoleDB 获取角色存储 12 | func GetRoleDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(Role)) 14 | } 15 | 16 | // SchemaRole 角色对象 17 | type SchemaRole schema.Role 18 | 19 | // ToRole 转换为角色实体 20 | func (a SchemaRole) ToRole() *Role { 21 | item := new(Role) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // Role 角色实体 27 | type Role struct { 28 | Model 29 | Name string `gorm:"column:name;size:100;index;default:'';not null;"` // 角色名称 30 | Sequence int `gorm:"column:sequence;index;default:0;not null;"` // 排序值 31 | Memo *string `gorm:"column:memo;size:1024;"` // 备注 32 | Status int `gorm:"column:status;index;default:0;not null;"` // 状态(1:启用 2:禁用) 33 | Creator string `gorm:"column:creator;size:36;"` // 创建者 34 | } 35 | 36 | // TableName 表名 37 | func (a Role) TableName() string { 38 | return a.Model.TableName("role") 39 | } 40 | 41 | // ToSchemaRole 转换为角色对象 42 | func (a Role) ToSchemaRole() *schema.Role { 43 | item := new(schema.Role) 44 | util.StructMapToStruct(a, item) 45 | return item 46 | } 47 | 48 | // Roles 角色实体列表 49 | type Roles []*Role 50 | 51 | // ToSchemaRoles 转换为角色对象列表 52 | func (a Roles) ToSchemaRoles() []*schema.Role { 53 | list := make([]*schema.Role, len(a)) 54 | for i, item := range a { 55 | list[i] = item.ToSchemaRole() 56 | } 57 | return list 58 | } 59 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_role_menu.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetRoleMenuDB 角色菜单 12 | func GetRoleMenuDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(RoleMenu)) 14 | } 15 | 16 | // SchemaRoleMenu 角色菜单 17 | type SchemaRoleMenu schema.RoleMenu 18 | 19 | // ToRoleMenu 转换为角色菜单实体 20 | func (a SchemaRoleMenu) ToRoleMenu() *RoleMenu { 21 | item := new(RoleMenu) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // RoleMenu 角色菜单实体 27 | type RoleMenu struct { 28 | Model 29 | RoleID string `gorm:"column:role_id;size:36;index;default:'';not null;"` // 角色ID 30 | MenuID string `gorm:"column:menu_id;size:36;index;default:'';not null;"` // 菜单ID 31 | ActionID string `gorm:"column:action_id;size:36;index;default:'';not null;"` // 动作ID 32 | } 33 | 34 | // TableName 表名 35 | func (a RoleMenu) TableName() string { 36 | return a.Model.TableName("role_menu") 37 | } 38 | 39 | // ToSchemaRoleMenu 转换为角色菜单对象 40 | func (a RoleMenu) ToSchemaRoleMenu() *schema.RoleMenu { 41 | item := new(schema.RoleMenu) 42 | util.StructMapToStruct(a, item) 43 | return item 44 | } 45 | 46 | // RoleMenus 角色菜单列表 47 | type RoleMenus []*RoleMenu 48 | 49 | // ToSchemaRoleMenus 转换为角色菜单对象列表 50 | func (a RoleMenus) ToSchemaRoleMenus() []*schema.RoleMenu { 51 | list := make([]*schema.RoleMenu, len(a)) 52 | for i, item := range a { 53 | list[i] = item.ToSchemaRoleMenu() 54 | } 55 | return list 56 | } 57 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_setting.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 9 | "github.com/jinzhu/gorm" 10 | ) 11 | 12 | // GetSettingDB 获取demo存储 13 | func GetSettingDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 14 | return GetDBWithModel(ctx, defDB, new(Setting)) 15 | } 16 | 17 | // SchemaSetting demo对象 18 | type SchemaSetting schema.Setting 19 | 20 | // ToSetting 转换为demo实体 21 | func (a SchemaSetting) ToSetting() *Setting { 22 | item := new(Setting) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // Setting demo实体 28 | type Setting struct { 29 | Key string `gorm:"column:key;size:64;unique;default:'';not null;"` // 键 30 | Value string `gorm:"column:value;type:longtext;not null;"` // 值 31 | CreatedAt time.Time `gorm:"column:created_at;index;"` 32 | UpdatedAt time.Time `gorm:"column:updated_at;index;"` 33 | } 34 | 35 | // TableName 表名 36 | func (a Setting) TableName() string { 37 | return Model{}.TableName("setting") 38 | } 39 | 40 | // ToSchemaSetting 转换为demo对象 41 | func (a Setting) ToSchemaSetting() *schema.Setting { 42 | item := new(schema.Setting) 43 | util.StructMapToStruct(a, item) 44 | return item 45 | } 46 | 47 | // Settings demo列表 48 | type Settings []*Setting 49 | 50 | // ToSchemaSettings 转换为demo对象列表 51 | func (a Settings) ToSchemaSettings() []*schema.Setting { 52 | list := make([]*schema.Setting, len(a)) 53 | for i, item := range a { 54 | list[i] = item.ToSchemaSetting() 55 | } 56 | return list 57 | } 58 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_user.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetUserDB 获取用户存储 12 | func GetUserDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(User)) 14 | } 15 | 16 | // SchemaUser 用户对象 17 | type SchemaUser schema.User 18 | 19 | // ToUser 转换为用户实体 20 | func (a SchemaUser) ToUser() *User { 21 | item := new(User) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // User 用户实体 27 | type User struct { 28 | Model 29 | UserName string `gorm:"column:user_name;size:64;index;default:'';not null;"` // 用户名 30 | RealName string `gorm:"column:real_name;size:64;index;default:'';not null;"` // 真实姓名 31 | Password string `gorm:"column:password;size:40;default:'';not null;"` // 密码(sha1(md5(明文))加密) 32 | Email *string `gorm:"column:email;size:255;index;"` // 邮箱 33 | Phone *string `gorm:"column:phone;size:20;index;"` // 手机号 34 | Status int `gorm:"column:status;index;default:0;not null;"` // 状态(1:启用 2:停用) 35 | Creator string `gorm:"column:creator;size:36;"` // 创建者 36 | } 37 | 38 | // TableName 表名 39 | func (a User) TableName() string { 40 | return a.Model.TableName("user") 41 | } 42 | 43 | // ToSchemaUser 转换为用户对象 44 | func (a User) ToSchemaUser() *schema.User { 45 | item := new(schema.User) 46 | util.StructMapToStruct(a, item) 47 | return item 48 | } 49 | 50 | // Users 用户实体列表 51 | type Users []*User 52 | 53 | // ToSchemaUsers 转换为用户对象列表 54 | func (a Users) ToSchemaUsers() []*schema.User { 55 | list := make([]*schema.User, len(a)) 56 | for i, item := range a { 57 | list[i] = item.ToSchemaUser() 58 | } 59 | return list 60 | } 61 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/e_user_role.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | ) 10 | 11 | // GetUserRoleDB 获取用户角色关联存储 12 | func GetUserRoleDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 13 | return GetDBWithModel(ctx, defDB, new(UserRole)) 14 | } 15 | 16 | // SchemaUserRole 用户角色 17 | type SchemaUserRole schema.UserRole 18 | 19 | // ToUserRole 转换为角色菜单实体 20 | func (a SchemaUserRole) ToUserRole() *UserRole { 21 | item := new(UserRole) 22 | util.StructMapToStruct(a, item) 23 | return item 24 | } 25 | 26 | // UserRole 用户角色关联实体 27 | type UserRole struct { 28 | Model 29 | UserID string `gorm:"column:user_id;size:36;index;default:'';not null;"` // 用户内码 30 | RoleID string `gorm:"column:role_id;size:36;index;default:'';not null;"` // 角色内码 31 | } 32 | 33 | // TableName 表名 34 | func (a UserRole) TableName() string { 35 | return a.Model.TableName("user_role") 36 | } 37 | 38 | // ToSchemaUserRole 转换为用户角色对象 39 | func (a UserRole) ToSchemaUserRole() *schema.UserRole { 40 | item := new(schema.UserRole) 41 | util.StructMapToStruct(a, item) 42 | return item 43 | } 44 | 45 | // UserRoles 用户角色关联列表 46 | type UserRoles []*UserRole 47 | 48 | // ToSchemaUserRoles 转换为用户角色对象列表 49 | func (a UserRoles) ToSchemaUserRoles() []*schema.UserRole { 50 | list := make([]*schema.UserRole, len(a)) 51 | for i, item := range a { 52 | list[i] = item.ToSchemaUserRole() 53 | } 54 | return list 55 | } 56 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/entity/entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 6 | "time" 7 | 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/icontext" 10 | "github.com/jinzhu/gorm" 11 | ) 12 | 13 | // Model base model 14 | type Model struct { 15 | ID string `gorm:"column:id;primary_key;size:36;"` 16 | CreatedAt time.Time `gorm:"column:created_at;index;"` 17 | UpdatedAt time.Time `gorm:"column:updated_at;index;"` 18 | DeletedAt *time.Time `gorm:"column:deleted_at;index;"` 19 | } 20 | 21 | // TableName table name 22 | func (Model) TableName(name string) string { 23 | return ginplus.GetTableWithPrefix(name) 24 | } 25 | 26 | // GetDB ... 27 | func GetDB(ctx context.Context, defDB *gorm.DB) *gorm.DB { 28 | trans, ok := icontext.FromTrans(ctx) 29 | if ok && !icontext.FromNoTrans(ctx) { 30 | db, ok := trans.(*gorm.DB) 31 | if ok { 32 | if icontext.FromTransLock(ctx) { 33 | if dbType := config.C.Gorm.DBType; dbType == "mysql" || 34 | dbType == "postgres" { 35 | db = db.Set("gorm:query_option", "FOR UPDATE") 36 | } 37 | } 38 | return db 39 | } 40 | } 41 | return defDB 42 | } 43 | 44 | // GetDBWithModel ... 45 | func GetDBWithModel(ctx context.Context, defDB *gorm.DB, m interface{}) *gorm.DB { 46 | return GetDB(ctx, defDB).Model(m) 47 | } 48 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/gorm.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 6 | "strings" 7 | "time" 8 | 9 | "github.com/jinzhu/gorm" 10 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 11 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/entity" 12 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 13 | 14 | // gorm存储注入 15 | _ "github.com/jinzhu/gorm/dialects/mysql" 16 | _ "github.com/jinzhu/gorm/dialects/postgres" 17 | _ "github.com/jinzhu/gorm/dialects/sqlite" 18 | ) 19 | 20 | // Config 配置参数 21 | type Config struct { 22 | Debug bool 23 | DBType string 24 | DSN string 25 | MaxLifetime int 26 | MaxOpenConns int 27 | MaxIdleConns int 28 | } 29 | 30 | // NewDB 创建DB实例 31 | func NewDB(c *Config) (*gorm.DB, func(), error) { 32 | db, err := gorm.Open(c.DBType, c.DSN) 33 | if err != nil { 34 | return nil, nil, err 35 | } 36 | 37 | if c.Debug { 38 | db = db.Debug() 39 | } 40 | 41 | cleanFunc := func() { 42 | err := db.Close() 43 | if err != nil { 44 | logger.Errorf(context.Background(), "Gorm db close error: %s", err.Error()) 45 | } 46 | } 47 | 48 | err = db.DB().Ping() 49 | if err != nil { 50 | return nil, cleanFunc, err 51 | } 52 | 53 | db.DB().SetMaxIdleConns(c.MaxIdleConns) 54 | db.DB().SetMaxOpenConns(c.MaxOpenConns) 55 | db.DB().SetConnMaxLifetime(time.Duration(c.MaxLifetime) * time.Second) 56 | return db, cleanFunc, nil 57 | } 58 | 59 | // AutoMigrate 自动映射数据表 60 | func AutoMigrate(db *gorm.DB) error { 61 | if dbType := config.C.Gorm.DBType; strings.ToLower(dbType) == "mysql" { 62 | db = db.Set("gorm:table_options", "ENGINE=InnoDB") 63 | } 64 | ginplus.SetTablePrefix(ginplus.GetDefaultAppId()) 65 | err := db.AutoMigrate( 66 | new(entity.Demo), 67 | new(entity.MenuAction), 68 | new(entity.MenuActionResource), 69 | new(entity.Menu), 70 | new(entity.RoleMenu), 71 | new(entity.Role), 72 | new(entity.UserRole), 73 | new(entity.User), 74 | new(entity.PageManager), 75 | new(entity.PageVersionHistory), 76 | new(entity.Setting), 77 | new(entity.GPlatform), 78 | new(entity.GTplMall), 79 | ).Error 80 | return err 81 | } 82 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/m_app.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/ginplus" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/entity" 8 | "github.com/google/wire" 9 | "github.com/jinzhu/gorm" 10 | ) 11 | 12 | var _ model.IApp = (*App)(nil) 13 | 14 | var AppSet = wire.NewSet(wire.Struct(new(App), "*"), wire.Bind(new(model.IApp), new(*App))) 15 | 16 | type App struct { 17 | DB *gorm.DB 18 | } 19 | 20 | // 创建对应的应用表 21 | func (a *App) Init(ctx context.Context, appId string) error { 22 | ginplus.SetTablePrefix(appId) 23 | err := a.DB.AutoMigrate( 24 | new(entity.Demo), 25 | new(entity.MenuAction), 26 | new(entity.MenuActionResource), 27 | new(entity.Menu), 28 | new(entity.RoleMenu), 29 | new(entity.Role), 30 | new(entity.UserRole), 31 | new(entity.User), 32 | new(entity.PageManager), 33 | new(entity.PageVersionHistory), 34 | new(entity.Setting), 35 | ).Error 36 | if err != nil { 37 | return err 38 | } 39 | return nil 40 | } 41 | 42 | func (a *App) Query(ctx context.Context, appId string) (bool, error) { 43 | ginplus.SetTablePrefix(appId) 44 | settingTableExists := a.DB.HasTable(new(entity.Setting)) 45 | return settingTableExists, nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/m_demo.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/entity" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 10 | "github.com/google/wire" 11 | "github.com/jinzhu/gorm" 12 | ) 13 | 14 | var _ model.IDemo = (*Demo)(nil) 15 | 16 | // DemoSet 注入Demo 17 | var DemoSet = wire.NewSet(wire.Struct(new(Demo), "*"), wire.Bind(new(model.IDemo), new(*Demo))) 18 | 19 | // Demo 示例存储 20 | type Demo struct { 21 | DB *gorm.DB 22 | } 23 | 24 | func (a *Demo) getQueryOption(opts ...schema.DemoQueryOptions) schema.DemoQueryOptions { 25 | var opt schema.DemoQueryOptions 26 | if len(opts) > 0 { 27 | opt = opts[0] 28 | } 29 | return opt 30 | } 31 | 32 | // Query 查询数据 33 | func (a *Demo) Query(ctx context.Context, params schema.DemoQueryParam, opts ...schema.DemoQueryOptions) (*schema.DemoQueryResult, error) { 34 | opt := a.getQueryOption(opts...) 35 | 36 | db := entity.GetDemoDB(ctx, a.DB) 37 | if v := params.Code; v != "" { 38 | db = db.Where("code=?", v) 39 | } 40 | if v := params.QueryValue; v != "" { 41 | v = "%" + v + "%" 42 | db = db.Where("code LIKE ? OR name LIKE ? OR memo LIKE ?", v, v, v) 43 | } 44 | 45 | opt.OrderFields = append(opt.OrderFields, schema.NewOrderField("id", schema.OrderByDESC)) 46 | db = db.Order(ParseOrder(opt.OrderFields)) 47 | 48 | var list entity.Demos 49 | pr, err := WrapPageQuery(ctx, db, params.PaginationParam, &list) 50 | if err != nil { 51 | return nil, errors.WithStack(err) 52 | } 53 | qr := &schema.DemoQueryResult{ 54 | PageResult: pr, 55 | Data: list.ToSchemaDemos(), 56 | } 57 | 58 | return qr, nil 59 | } 60 | 61 | // Get 查询指定数据 62 | func (a *Demo) Get(ctx context.Context, id string, opts ...schema.DemoQueryOptions) (*schema.Demo, error) { 63 | db := entity.GetDemoDB(ctx, a.DB).Where("id=?", id) 64 | var item entity.Demo 65 | ok, err := FindOne(ctx, db, &item) 66 | if err != nil { 67 | return nil, errors.WithStack(err) 68 | } else if !ok { 69 | return nil, nil 70 | } 71 | 72 | return item.ToSchemaDemo(), nil 73 | } 74 | 75 | // Create 创建数据 76 | func (a *Demo) Create(ctx context.Context, item schema.Demo) error { 77 | eitem := entity.SchemaDemo(item).ToDemo() 78 | result := entity.GetDemoDB(ctx, a.DB).Create(eitem) 79 | return errors.WithStack(result.Error) 80 | } 81 | 82 | // Update 更新数据 83 | func (a *Demo) Update(ctx context.Context, id string, item schema.Demo) error { 84 | eitem := entity.SchemaDemo(item).ToDemo() 85 | result := entity.GetDemoDB(ctx, a.DB).Where("id=?", id).Updates(eitem) 86 | return errors.WithStack(result.Error) 87 | } 88 | 89 | // Delete 删除数据 90 | func (a *Demo) Delete(ctx context.Context, id string) error { 91 | result := entity.GetDemoDB(ctx, a.DB).Where("id=?", id).Delete(entity.Demo{}) 92 | return errors.WithStack(result.Error) 93 | } 94 | 95 | // UpdateStatus 更新状态 96 | func (a *Demo) UpdateStatus(ctx context.Context, id string, status int) error { 97 | result := entity.GetDemoDB(ctx, a.DB).Where("id=?", id).Update("status", status) 98 | return errors.WithStack(result.Error) 99 | } 100 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/m_page_version_history.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/entity" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 9 | "github.com/google/wire" 10 | "github.com/jinzhu/gorm" 11 | ) 12 | 13 | var _ model.IPageVersionHistory = (*PageVersionHistory)(nil) 14 | 15 | // MenuSet 注入Menu 16 | var PageVersionHistorySet = wire.NewSet(wire.Struct(new(PageVersionHistory), "*"), wire.Bind(new(model.IPageVersionHistory), new(*PageVersionHistory))) 17 | 18 | // Menu 菜单存储 19 | type PageVersionHistory struct { 20 | DB *gorm.DB 21 | } 22 | 23 | func (a *PageVersionHistory) getQueryOption(opts ...schema.PageVersionHistoryQueryOptions) schema.PageVersionHistoryQueryOptions { 24 | var opt schema.PageVersionHistoryQueryOptions 25 | if len(opts) > 0 { 26 | opt = opts[0] 27 | } 28 | return opt 29 | } 30 | 31 | // 查询数据 32 | func (a *PageVersionHistory) Query(ctx context.Context, params schema.PageVersionHistoryQueryParam, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistoryQueryResult, error) { 33 | opt := a.getQueryOption(opts...) 34 | 35 | db := entity.GetPageVersionHistoryDB(ctx, a.DB) 36 | 37 | if v := params.PageManagerId; v != "" { 38 | db = db.Where(" page_manager_id = ?", v) 39 | } 40 | 41 | opt.OrderFields = append(opt.OrderFields, schema.NewOrderField("id", schema.OrderByDESC)) 42 | db = db.Order(ParseOrder(opt.OrderFields)) 43 | 44 | var list entity.PageVersionHistorys 45 | pr, err := WrapPageQuery(ctx, db, params.PaginationParam, &list) 46 | if err != nil { 47 | return nil, errors.WithStack(err) 48 | } 49 | 50 | qr := &schema.PageVersionHistoryQueryResult{ 51 | PageResult: pr, 52 | Data: list.ToSchemaPageVersionHistorys(), 53 | } 54 | return qr, nil 55 | } 56 | 57 | // 查询指定数据 58 | func (a *PageVersionHistory) Get(ctx context.Context, id string, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistory, error) { 59 | db := entity.GetPageVersionHistoryDB(ctx, a.DB).Where("id=?", id) 60 | var item entity.PageVersionHistory 61 | ok, err := FindOne(ctx, db, &item) 62 | if err != nil { 63 | return nil, errors.WithStack(err) 64 | } else if !ok { 65 | return nil, nil 66 | } 67 | 68 | return item.ToSchemaPageVersionHistory(), nil 69 | } 70 | 71 | // 创建数据 72 | func (a *PageVersionHistory) Create(ctx context.Context, item schema.PageVersionHistory) error { 73 | sitem := entity.SchemaPageVersionHistory(item) 74 | result := entity.GetPageVersionHistoryDB(ctx, a.DB).Create(sitem.ToPageVersionHistory()) 75 | return errors.WithStack(result.Error) 76 | } 77 | 78 | // 更新数据 79 | func (a *PageVersionHistory) Update(ctx context.Context, id string, item schema.PageVersionHistory) error { 80 | eitem := entity.SchemaPageVersionHistory(item).ToPageVersionHistory() 81 | result := entity.GetPageVersionHistoryDB(ctx, a.DB).Where("id=?", id).Updates(eitem) 82 | return errors.WithStack(result.Error) 83 | } 84 | 85 | // 删除数据 86 | func (a *PageVersionHistory) Delete(ctx context.Context, id string) error { 87 | result := entity.GetPageVersionHistoryDB(ctx, a.DB).Where("id=?", id).Delete(entity.PageVersionHistory{}) 88 | return errors.WithStack(result.Error) 89 | } 90 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/m_role_menu.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/entity" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 9 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 10 | "github.com/google/wire" 11 | "github.com/jinzhu/gorm" 12 | ) 13 | 14 | var _ model.IRoleMenu = (*RoleMenu)(nil) 15 | 16 | // RoleMenuSet 注入RoleMenu 17 | var RoleMenuSet = wire.NewSet(wire.Struct(new(RoleMenu), "*"), wire.Bind(new(model.IRoleMenu), new(*RoleMenu))) 18 | 19 | // RoleMenu 角色菜单存储 20 | type RoleMenu struct { 21 | DB *gorm.DB 22 | } 23 | 24 | func (a *RoleMenu) getQueryOption(opts ...schema.RoleMenuQueryOptions) schema.RoleMenuQueryOptions { 25 | var opt schema.RoleMenuQueryOptions 26 | if len(opts) > 0 { 27 | opt = opts[0] 28 | } 29 | return opt 30 | } 31 | 32 | // Query 查询数据 33 | func (a *RoleMenu) Query(ctx context.Context, params schema.RoleMenuQueryParam, opts ...schema.RoleMenuQueryOptions) (*schema.RoleMenuQueryResult, error) { 34 | opt := a.getQueryOption(opts...) 35 | 36 | db := entity.GetRoleMenuDB(ctx, a.DB) 37 | if v := params.RoleID; v != "" { 38 | db = db.Where("role_id=?", v) 39 | } 40 | if v := params.RoleIDs; len(v) > 0 { 41 | db = db.Where("role_id IN (?)", v) 42 | } 43 | 44 | opt.OrderFields = append(opt.OrderFields, schema.NewOrderField("id", schema.OrderByDESC)) 45 | db = db.Order(ParseOrder(opt.OrderFields)) 46 | 47 | var list entity.RoleMenus 48 | pr, err := WrapPageQuery(ctx, db, params.PaginationParam, &list) 49 | if err != nil { 50 | return nil, errors.WithStack(err) 51 | } 52 | qr := &schema.RoleMenuQueryResult{ 53 | PageResult: pr, 54 | Data: list.ToSchemaRoleMenus(), 55 | } 56 | 57 | return qr, nil 58 | } 59 | 60 | // Get 查询指定数据 61 | func (a *RoleMenu) Get(ctx context.Context, id string, opts ...schema.RoleMenuQueryOptions) (*schema.RoleMenu, error) { 62 | db := entity.GetRoleMenuDB(ctx, a.DB).Where("id=?", id) 63 | var item entity.RoleMenu 64 | ok, err := FindOne(ctx, db, &item) 65 | if err != nil { 66 | return nil, errors.WithStack(err) 67 | } else if !ok { 68 | return nil, nil 69 | } 70 | 71 | return item.ToSchemaRoleMenu(), nil 72 | } 73 | 74 | // Create 创建数据 75 | func (a *RoleMenu) Create(ctx context.Context, item schema.RoleMenu) error { 76 | eitem := entity.SchemaRoleMenu(item).ToRoleMenu() 77 | result := entity.GetRoleMenuDB(ctx, a.DB).Create(eitem) 78 | return errors.WithStack(result.Error) 79 | } 80 | 81 | // Update 更新数据 82 | func (a *RoleMenu) Update(ctx context.Context, id string, item schema.RoleMenu) error { 83 | eitem := entity.SchemaRoleMenu(item).ToRoleMenu() 84 | result := entity.GetRoleMenuDB(ctx, a.DB).Where("id=?", id).Updates(eitem) 85 | return errors.WithStack(result.Error) 86 | } 87 | 88 | // Delete 删除数据 89 | func (a *RoleMenu) Delete(ctx context.Context, id string) error { 90 | result := entity.GetRoleMenuDB(ctx, a.DB).Where("id=?", id).Delete(entity.RoleMenu{}) 91 | return errors.WithStack(result.Error) 92 | } 93 | 94 | // DeleteByRoleID 根据角色ID删除数据 95 | func (a *RoleMenu) DeleteByRoleID(ctx context.Context, roleID string) error { 96 | result := entity.GetRoleMenuDB(ctx, a.DB).Where("role_id=?", roleID).Delete(entity.RoleMenu{}) 97 | return errors.WithStack(result.Error) 98 | } 99 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/m_setting.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/google/wire" 7 | "github.com/jinzhu/gorm" 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 9 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/gorm/entity" 10 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 11 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 12 | ) 13 | 14 | var _ model.ISetting = (*Setting)(nil) 15 | 16 | // SettingSet 注入Setting 17 | var SettingSet = wire.NewSet(wire.Struct(new(Setting), "*"), wire.Bind(new(model.ISetting), new(*Setting))) 18 | 19 | // Setting 示例存储 20 | type Setting struct { 21 | DB *gorm.DB 22 | } 23 | 24 | // Query 查询数据 25 | func (a *Setting) Query(ctx context.Context) (*schema.SettingQueryResult, error) { 26 | db := entity.GetSettingDB(ctx, a.DB) 27 | var list entity.Settings 28 | err := db.Find(&list).Error 29 | if err != nil { 30 | return nil, errors.WithStack(err) 31 | } 32 | qr := &schema.SettingQueryResult{ 33 | Data: list.ToSchemaSettings(), 34 | } 35 | return qr, nil 36 | } 37 | 38 | // Get 查询指定数据 39 | func (a *Setting) Get(ctx context.Context, id string) (*schema.Setting, error) { 40 | db := entity.GetSettingDB(ctx, a.DB).Where("key=?", id) 41 | var item entity.Setting 42 | ok, err := FindOne(ctx, db, &item) 43 | if err != nil { 44 | return nil, errors.WithStack(err) 45 | } else if !ok { 46 | return nil, nil 47 | } 48 | 49 | return item.ToSchemaSetting(), nil 50 | } 51 | 52 | // Create 创建数据 53 | func (a *Setting) Create(ctx context.Context, item schema.Setting) error { 54 | eitem := entity.SchemaSetting(item).ToSetting() 55 | db := entity.GetSettingDB(ctx, a.DB) 56 | result := db.Create(eitem) 57 | return errors.WithStack(result.Error) 58 | } 59 | 60 | // Update 更新数据 61 | func (a *Setting) Update(ctx context.Context, id string, item schema.Setting) error { 62 | eitem := entity.SchemaSetting(item).ToSetting() 63 | result := entity.GetSettingDB(ctx, a.DB).Where("id=?", id).Updates(eitem) 64 | return errors.WithStack(result.Error) 65 | } 66 | 67 | // Delete 删除数据 68 | func (a *Setting) Delete(ctx context.Context, id string) error { 69 | result := entity.GetSettingDB(ctx, a.DB).Where("id=?", id).Delete(entity.Setting{}) 70 | return errors.WithStack(result.Error) 71 | } 72 | 73 | func (a *Setting) Truncate(ctx context.Context) error { 74 | db := entity.GetSettingDB(ctx, a.DB).Delete(entity.Setting{}) 75 | //db := entity.GetSettingDB(ctx, a.DB).Exec("truncate table " + (entity.Setting{}).TableName()) 76 | if db.Error != nil { 77 | return db.Error 78 | } 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/m_trans.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/icontext" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 9 | "github.com/google/wire" 10 | "github.com/jinzhu/gorm" 11 | ) 12 | 13 | var _ model.ITrans = new(Trans) 14 | 15 | // TransSet 注入Trans 16 | var TransSet = wire.NewSet(wire.Struct(new(Trans), "*"), wire.Bind(new(model.ITrans), new(*Trans))) 17 | 18 | // Trans 事务管理 19 | type Trans struct { 20 | DB *gorm.DB 21 | } 22 | 23 | // Exec 执行事务 24 | func (a *Trans) Exec(ctx context.Context, fn func(context.Context) error) error { 25 | if _, ok := icontext.FromTrans(ctx); ok { 26 | return fn(ctx) 27 | } 28 | 29 | err := a.DB.Transaction(func(db *gorm.DB) error { 30 | return fn(icontext.NewTrans(ctx, db)) 31 | }) 32 | if err != nil { 33 | return errors.WithStack(err) 34 | } 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /internal/app/model/impl/gorm/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/google/wire" 4 | 5 | // ModelSet model注入 6 | var ModelSet = wire.NewSet( 7 | DemoSet, 8 | MenuActionResourceSet, 9 | MenuActionSet, 10 | MenuSet, 11 | RoleMenuSet, 12 | RoleSet, 13 | TransSet, 14 | UserRoleSet, 15 | UserSet, 16 | PageManagerSet, 17 | SettingSet, 18 | AppSet, 19 | GPlatformSet, 20 | GTplMallSet, 21 | ) 22 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_demo.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetDemoCollection 获取demo存储 13 | func GetDemoCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, Demo{}) 15 | } 16 | 17 | // SchemaDemo demo对象 18 | type SchemaDemo schema.Demo 19 | 20 | // ToDemo 转换为demo实体 21 | func (a SchemaDemo) ToDemo() *Demo { 22 | item := new(Demo) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // Demo demo实体 28 | type Demo struct { 29 | Model `bson:",inline"` 30 | Code string `bson:"code"` // 编号 31 | Name string `bson:"name"` // 名称 32 | Memo string `bson:"memo"` // 备注 33 | Status int `bson:"status"` // 状态(1:启用 2:停用) 34 | Creator string `bson:"creator"` // 创建者 35 | } 36 | 37 | // CollectionName 集合名 38 | func (a Demo) CollectionName() string { 39 | return a.Model.CollectionName("demo") 40 | } 41 | 42 | // CreateIndexes 创建索引 43 | func (a Demo) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 44 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 45 | {Keys: bson.M{"code": 1}}, 46 | {Keys: bson.M{"name": 1}}, 47 | {Keys: bson.M{"status": 1}}, 48 | }) 49 | } 50 | 51 | // ToSchemaDemo 转换为demo对象 52 | func (a Demo) ToSchemaDemo() *schema.Demo { 53 | item := new(schema.Demo) 54 | util.StructMapToStruct(a, item) 55 | return item 56 | } 57 | 58 | // Demos demo列表 59 | type Demos []*Demo 60 | 61 | // ToSchemaDemos 转换为demo对象列表 62 | func (a Demos) ToSchemaDemos() []*schema.Demo { 63 | list := make([]*schema.Demo, len(a)) 64 | for i, item := range a { 65 | list[i] = item.ToSchemaDemo() 66 | } 67 | return list 68 | } 69 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_menu.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetMenuCollection 获取Menu存储 13 | func GetMenuCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, Menu{}) 15 | } 16 | 17 | // SchemaMenu 菜单对象 18 | type SchemaMenu schema.Menu 19 | 20 | // ToMenu 转换为菜单实体 21 | func (a SchemaMenu) ToMenu() *Menu { 22 | item := new(Menu) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // Menu 菜单实体 28 | type Menu struct { 29 | Model `bson:",inline"` 30 | Name string `bson:"name"` // 菜单名称 31 | Sequence int `bson:"sequence"` // 排序值 32 | Icon string `bson:"icon"` // 菜单图标 33 | Router string `bson:"router"` // 访问路由 34 | ParentID string `bson:"parent_id"` // 父级内码 35 | ParentPath string `bson:"parent_path"` // 父级路径 36 | ShowStatus int `bson:"show_status"` // 状态(1:显示 2:隐藏) 37 | Status int `bson:"status"` // 状态(1:启用 2:禁用) 38 | Memo string `bson:"memo"` // 备注 39 | Creator string `bson:"creator"` // 创建人 40 | } 41 | 42 | // CollectionName 集合名 43 | func (a Menu) CollectionName() string { 44 | return a.Model.CollectionName("menu") 45 | } 46 | 47 | // CreateIndexes 创建索引 48 | func (a Menu) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 49 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 50 | {Keys: bson.M{"name": 1}}, 51 | {Keys: bson.M{"sequence": -1}}, 52 | {Keys: bson.M{"parent_id": 1}}, 53 | {Keys: bson.M{"parent_path": 1}}, 54 | {Keys: bson.M{"show_status": 1}}, 55 | {Keys: bson.M{"status": 1}}, 56 | }) 57 | } 58 | 59 | // ToSchemaMenu 转换为菜单对象 60 | func (a Menu) ToSchemaMenu() *schema.Menu { 61 | item := new(schema.Menu) 62 | util.StructMapToStruct(a, item) 63 | return item 64 | } 65 | 66 | // Menus 菜单实体列表 67 | type Menus []*Menu 68 | 69 | // ToSchemaMenus 转换为菜单对象列表 70 | func (a Menus) ToSchemaMenus() []*schema.Menu { 71 | list := make([]*schema.Menu, len(a)) 72 | for i, item := range a { 73 | list[i] = item.ToSchemaMenu() 74 | } 75 | return list 76 | } 77 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_menu_action.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetMenuActionCollection 获取MenuAction存储 13 | func GetMenuActionCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, MenuAction{}) 15 | } 16 | 17 | // SchemaMenuAction 菜单动作 18 | type SchemaMenuAction schema.MenuAction 19 | 20 | // ToMenuAction 转换为菜单动作实体 21 | func (a SchemaMenuAction) ToMenuAction() *MenuAction { 22 | item := new(MenuAction) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // MenuAction 菜单动作实体 28 | type MenuAction struct { 29 | Model `bson:",inline"` 30 | MenuID string `bson:"menu_id"` // 菜单ID 31 | Code string `bson:"code"` // 动作编号 32 | Name string `bson:"name"` // 动作名称 33 | } 34 | 35 | // CollectionName 集合名 36 | func (a MenuAction) CollectionName() string { 37 | return a.Model.CollectionName("menu_action") 38 | } 39 | 40 | // CreateIndexes 创建索引 41 | func (a MenuAction) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 42 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 43 | {Keys: bson.M{"menu_id": 1}}, 44 | }) 45 | } 46 | 47 | // ToSchemaMenuAction 转换为菜单动作对象 48 | func (a MenuAction) ToSchemaMenuAction() *schema.MenuAction { 49 | item := new(schema.MenuAction) 50 | util.StructMapToStruct(a, item) 51 | return item 52 | } 53 | 54 | // MenuActions 菜单动作列表 55 | type MenuActions []*MenuAction 56 | 57 | // ToSchemaMenuActions 转换为菜单动作对象列表 58 | func (a MenuActions) ToSchemaMenuActions() []*schema.MenuAction { 59 | list := make([]*schema.MenuAction, len(a)) 60 | for i, item := range a { 61 | list[i] = item.ToSchemaMenuAction() 62 | } 63 | return list 64 | } 65 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_menu_action_resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetMenuActionResourceCollection 获取MenuActionResource存储 13 | func GetMenuActionResourceCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, MenuActionResource{}) 15 | } 16 | 17 | // SchemaMenuActionResource 菜单动作关联资源 18 | type SchemaMenuActionResource schema.MenuActionResource 19 | 20 | // ToMenuActionResource 转换为菜单动作关联资源实体 21 | func (a SchemaMenuActionResource) ToMenuActionResource() *MenuActionResource { 22 | item := new(MenuActionResource) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // MenuActionResource 菜单动作关联资源实体 28 | type MenuActionResource struct { 29 | Model `bson:",inline"` 30 | ActionID string `bson:"action_id"` // 菜单动作ID 31 | Method string `bson:"method"` // 资源请求方式(支持正则) 32 | Path string `bson:"path"` // 资源请求路径(支持/:id匹配) 33 | } 34 | 35 | // CollectionName 集合名 36 | func (a MenuActionResource) CollectionName() string { 37 | return a.Model.CollectionName("menu_action_resource") 38 | } 39 | 40 | // CreateIndexes 创建索引 41 | func (a MenuActionResource) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 42 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 43 | {Keys: bson.M{"action_id": 1}}, 44 | }) 45 | } 46 | 47 | // ToSchemaMenuActionResource 转换为菜单动作关联资源对象 48 | func (a MenuActionResource) ToSchemaMenuActionResource() *schema.MenuActionResource { 49 | item := new(schema.MenuActionResource) 50 | util.StructMapToStruct(a, item) 51 | return item 52 | } 53 | 54 | // MenuActionResources 菜单动作关联资源列表 55 | type MenuActionResources []*MenuActionResource 56 | 57 | // ToSchemaMenuActionResources 转换为菜单动作关联资源对象列表 58 | func (a MenuActionResources) ToSchemaMenuActionResources() []*schema.MenuActionResource { 59 | list := make([]*schema.MenuActionResource, len(a)) 60 | for i, item := range a { 61 | list[i] = item.ToSchemaMenuActionResource() 62 | } 63 | return list 64 | } 65 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_role.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetRoleCollection 获取Role存储 13 | func GetRoleCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, Role{}) 15 | } 16 | 17 | // SchemaRole 角色对象 18 | type SchemaRole schema.Role 19 | 20 | // ToRole 转换为角色实体 21 | func (a SchemaRole) ToRole() *Role { 22 | item := new(Role) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // Role 角色实体 28 | type Role struct { 29 | Model `bson:",inline"` 30 | Name string `bson:"name"` // 角色名称 31 | Sequence int `bson:"sequence"` // 排序值 32 | Memo string `bson:"memo"` // 备注 33 | Status int `bson:"status"` // 状态(1:启用 2:禁用) 34 | Creator string `bson:"creator"` // 创建者 35 | } 36 | 37 | // CollectionName 集合名 38 | func (a Role) CollectionName() string { 39 | return a.Model.CollectionName("role") 40 | } 41 | 42 | // CreateIndexes 创建索引 43 | func (a Role) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 44 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 45 | {Keys: bson.M{"name": 1}}, 46 | {Keys: bson.M{"sequence": -1}}, 47 | {Keys: bson.M{"status": 1}}, 48 | }) 49 | } 50 | 51 | // ToSchemaRole 转换为角色对象 52 | func (a Role) ToSchemaRole() *schema.Role { 53 | item := new(schema.Role) 54 | util.StructMapToStruct(a, item) 55 | return item 56 | } 57 | 58 | // Roles 角色实体列表 59 | type Roles []*Role 60 | 61 | // ToSchemaRoles 转换为角色对象列表 62 | func (a Roles) ToSchemaRoles() []*schema.Role { 63 | list := make([]*schema.Role, len(a)) 64 | for i, item := range a { 65 | list[i] = item.ToSchemaRole() 66 | } 67 | return list 68 | } 69 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_role_menu.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetRoleMenuCollection 获取RoleMenu存储 13 | func GetRoleMenuCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, RoleMenu{}) 15 | } 16 | 17 | // SchemaRoleMenu 角色菜单 18 | type SchemaRoleMenu schema.RoleMenu 19 | 20 | // ToRoleMenu 转换为角色菜单实体 21 | func (a SchemaRoleMenu) ToRoleMenu() *RoleMenu { 22 | item := new(RoleMenu) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // RoleMenu 角色菜单实体 28 | type RoleMenu struct { 29 | Model `bson:",inline"` 30 | RoleID string `bson:"role_id"` // 角色ID 31 | MenuID string `bson:"menu_id"` // 菜单ID 32 | ActionID string `bson:"action_id"` // 动作ID 33 | } 34 | 35 | // CollectionName 集合名 36 | func (a RoleMenu) CollectionName() string { 37 | return a.Model.CollectionName("role_menu") 38 | } 39 | 40 | // CreateIndexes 创建索引 41 | func (a RoleMenu) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 42 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 43 | {Keys: bson.M{"role_id": 1}}, 44 | {Keys: bson.M{"menu_id": 1}}, 45 | {Keys: bson.M{"action_id": 1}}, 46 | }) 47 | } 48 | 49 | // ToSchemaRoleMenu 转换为角色菜单对象 50 | func (a RoleMenu) ToSchemaRoleMenu() *schema.RoleMenu { 51 | item := new(schema.RoleMenu) 52 | util.StructMapToStruct(a, item) 53 | return item 54 | } 55 | 56 | // RoleMenus 角色菜单列表 57 | type RoleMenus []*RoleMenu 58 | 59 | // ToSchemaRoleMenus 转换为角色菜单对象列表 60 | func (a RoleMenus) ToSchemaRoleMenus() []*schema.RoleMenu { 61 | list := make([]*schema.RoleMenu, len(a)) 62 | for i, item := range a { 63 | list[i] = item.ToSchemaRoleMenu() 64 | } 65 | return list 66 | } 67 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_user.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetUserCollection 获取User存储 13 | func GetUserCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, User{}) 15 | } 16 | 17 | // SchemaUser 用户对象 18 | type SchemaUser schema.User 19 | 20 | // ToUser 转换为用户实体 21 | func (a SchemaUser) ToUser() *User { 22 | item := new(User) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // User 用户实体 28 | type User struct { 29 | Model `bson:",inline"` 30 | UserName string `bson:"user_name"` // 用户名 31 | RealName string `bson:"real_name"` // 真实姓名 32 | Password string `bson:"password"` // 密码(sha1(md5(明文))加密) 33 | Email string `bson:"email"` // 邮箱 34 | Phone string `bson:"phone"` // 手机号 35 | Status int `bson:"status"` // 状态(1:启用 2:停用) 36 | Creator string `bson:"creator"` // 创建者 37 | } 38 | 39 | // CollectionName 集合名 40 | func (a User) CollectionName() string { 41 | return a.Model.CollectionName("user") 42 | } 43 | 44 | // CreateIndexes 创建索引 45 | func (a User) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 46 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 47 | {Keys: bson.M{"user_name": 1}}, 48 | {Keys: bson.M{"real_name": 1}}, 49 | {Keys: bson.M{"status": 1}}, 50 | }) 51 | } 52 | 53 | // ToSchemaUser 转换为用户对象 54 | func (a User) ToSchemaUser() *schema.User { 55 | item := new(schema.User) 56 | util.StructMapToStruct(a, item) 57 | return item 58 | } 59 | 60 | // Users 用户实体列表 61 | type Users []*User 62 | 63 | // ToSchemaUsers 转换为用户对象列表 64 | func (a Users) ToSchemaUsers() []*schema.User { 65 | list := make([]*schema.User, len(a)) 66 | for i, item := range a { 67 | list[i] = item.ToSchemaUser() 68 | } 69 | return list 70 | } 71 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/e_user_role.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | ) 11 | 12 | // GetUserRoleCollection 获取UserRole存储 13 | func GetUserRoleCollection(ctx context.Context, cli *mongo.Client) *mongo.Collection { 14 | return GetCollection(ctx, cli, UserRole{}) 15 | } 16 | 17 | // SchemaUserRole 用户角色 18 | type SchemaUserRole schema.UserRole 19 | 20 | // ToUserRole 转换为角色菜单实体 21 | func (a SchemaUserRole) ToUserRole() *UserRole { 22 | item := new(UserRole) 23 | util.StructMapToStruct(a, item) 24 | return item 25 | } 26 | 27 | // UserRole 用户角色关联实体 28 | type UserRole struct { 29 | Model `bson:",inline"` 30 | UserID string `bson:"user_id"` // 用户内码 31 | RoleID string `bson:"role_id"` // 角色内码 32 | } 33 | 34 | // CollectionName 集合名 35 | func (a UserRole) CollectionName() string { 36 | return a.Model.CollectionName("user_role") 37 | } 38 | 39 | // CreateIndexes 创建索引 40 | func (a UserRole) CreateIndexes(ctx context.Context, cli *mongo.Client) error { 41 | return a.Model.CreateIndexes(ctx, cli, a, []mongo.IndexModel{ 42 | {Keys: bson.M{"user_id": 1}}, 43 | {Keys: bson.M{"role_id": 1}}, 44 | }) 45 | } 46 | 47 | // ToSchemaUserRole 转换为用户角色对象 48 | func (a UserRole) ToSchemaUserRole() *schema.UserRole { 49 | item := new(schema.UserRole) 50 | util.StructMapToStruct(a, item) 51 | return item 52 | } 53 | 54 | // UserRoles 用户角色关联列表 55 | type UserRoles []*UserRole 56 | 57 | // ToSchemaUserRoles 转换为用户角色对象列表 58 | func (a UserRoles) ToSchemaUserRoles() []*schema.UserRole { 59 | list := make([]*schema.UserRole, len(a)) 60 | for i, item := range a { 61 | list[i] = item.ToSchemaUserRole() 62 | } 63 | return list 64 | } 65 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/entity/entity.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/tanjiancheng/gin-amis-admin/internal/app/config" 9 | "go.mongodb.org/mongo-driver/bson" 10 | "go.mongodb.org/mongo-driver/mongo" 11 | ) 12 | 13 | // Model base model 14 | type Model struct { 15 | ID string `bson:"_id"` 16 | CreatedAt time.Time `bson:"created_at"` 17 | UpdatedAt time.Time `bson:"updated_at"` 18 | DeletedAt *time.Time `bson:"deleted_at,omitempty"` 19 | } 20 | 21 | // CollectionName collection name 22 | func (Model) CollectionName(name string) string { 23 | return fmt.Sprintf("%s%s", config.C.Mongo.CollectionPrefix, name) 24 | } 25 | 26 | // CreateIndexes 创建索引 27 | func (Model) CreateIndexes(ctx context.Context, cli *mongo.Client, m Collectioner, indexes []mongo.IndexModel) error { 28 | models := []mongo.IndexModel{ 29 | {Keys: bson.M{"created_at": 1}}, 30 | {Keys: bson.M{"updated_at": 1}}, 31 | {Keys: bson.M{"deleted_at": 1}}, 32 | } 33 | if len(indexes) > 0 { 34 | models = append(models, indexes...) 35 | } 36 | _, err := GetCollection(ctx, cli, m).Indexes().CreateMany(ctx, models) 37 | return err 38 | } 39 | 40 | // Collectioner ... 41 | type Collectioner interface { 42 | CollectionName() string 43 | } 44 | 45 | // GetCollection ... 46 | func GetCollection(ctx context.Context, cli *mongo.Client, m Collectioner) *mongo.Collection { 47 | return cli.Database(config.C.Mongo.Database).Collection(m.CollectionName()) 48 | } 49 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/model/m_trans.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/icontext" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/errors" 9 | "github.com/google/wire" 10 | "go.mongodb.org/mongo-driver/mongo" 11 | ) 12 | 13 | var _ model.ITrans = new(Trans) 14 | 15 | // TransSet 注入Trans 16 | var TransSet = wire.NewSet(wire.Struct(new(Trans), "*"), wire.Bind(new(model.ITrans), new(*Trans))) 17 | 18 | // Trans 事务管理 19 | type Trans struct { 20 | Client *mongo.Client 21 | } 22 | 23 | // Exec 执行事务 24 | func (a *Trans) Exec(ctx context.Context, fn func(context.Context) error) error { 25 | if _, ok := icontext.FromTrans(ctx); ok { 26 | return fn(ctx) 27 | } 28 | 29 | session, err := a.Client.StartSession() 30 | if err != nil { 31 | return errors.WithStack(err) 32 | } 33 | defer session.EndSession(ctx) 34 | 35 | _, err = session.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) { 36 | err := fn(icontext.NewTrans(sessCtx, true)) 37 | return nil, err 38 | }) 39 | 40 | if err != nil { 41 | return errors.WithStack(err) 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/google/wire" 4 | 5 | // ModelSet model注入 6 | var ModelSet = wire.NewSet( 7 | DemoSet, 8 | MenuActionResourceSet, 9 | MenuActionSet, 10 | MenuSet, 11 | RoleMenuSet, 12 | RoleSet, 13 | TransSet, 14 | UserRoleSet, 15 | UserSet, 16 | ) 17 | -------------------------------------------------------------------------------- /internal/app/model/impl/mongo/mongo.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/model/impl/mongo/entity" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 9 | "go.mongodb.org/mongo-driver/mongo" 10 | "go.mongodb.org/mongo-driver/mongo/options" 11 | ) 12 | 13 | // Config 配置参数 14 | type Config struct { 15 | URI string 16 | Database string 17 | Timeout time.Duration 18 | } 19 | 20 | // NewClient 创建mongo客户端实例 21 | func NewClient(cfg *Config) (*mongo.Client, func(), error) { 22 | var ( 23 | ctx = context.Background() 24 | cancel context.CancelFunc 25 | ) 26 | 27 | if t := cfg.Timeout; t > 0 { 28 | ctx, cancel = context.WithTimeout(ctx, t) 29 | defer cancel() 30 | } 31 | 32 | cli, err := mongo.Connect(ctx, options.Client().ApplyURI(cfg.URI)) 33 | if err != nil { 34 | return nil, nil, err 35 | } 36 | 37 | cleanFunc := func() { 38 | err := cli.Disconnect(context.Background()) 39 | if err != nil { 40 | logger.Errorf(context.Background(), "Mongo disconnect error: %s", err.Error()) 41 | } 42 | } 43 | 44 | err = cli.Ping(context.Background(), nil) 45 | if err != nil { 46 | return nil, cleanFunc, err 47 | } 48 | return cli, cleanFunc, nil 49 | } 50 | 51 | // CreateIndexes 创建索引 52 | func CreateIndexes(ctx context.Context, cli *mongo.Client) error { 53 | return createIndexes( 54 | ctx, 55 | cli, 56 | new(entity.Demo), 57 | new(entity.MenuAction), 58 | new(entity.MenuActionResource), 59 | new(entity.Menu), 60 | new(entity.RoleMenu), 61 | new(entity.Role), 62 | new(entity.UserRole), 63 | new(entity.User), 64 | ) 65 | } 66 | 67 | type indexer interface { 68 | CreateIndexes(ctx context.Context, cli *mongo.Client) error 69 | } 70 | 71 | func createIndexes(ctx context.Context, cli *mongo.Client, indexes ...indexer) error { 72 | for _, idx := range indexes { 73 | err := idx.CreateIndexes(ctx, cli) 74 | if err != nil { 75 | return err 76 | } 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /internal/app/model/m_app.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type IApp interface { 8 | Init(ctx context.Context, appId string) error 9 | Query(ctx context.Context, appId string) (bool, error) 10 | } 11 | -------------------------------------------------------------------------------- /internal/app/model/m_demo.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IDemo demo存储接口 10 | type IDemo interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.DemoQueryParam, opts ...schema.DemoQueryOptions) (*schema.DemoQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.DemoQueryOptions) (*schema.Demo, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.Demo) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.Demo) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 更新状态 22 | UpdateStatus(ctx context.Context, id string, status int) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/model/m_g_platform.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | type IGPlatform interface { 10 | // 查询数据 11 | Query(ctx context.Context, params schema.GPlatformQueryParam, opts ...schema.GPlatformQueryOptions) (*schema.GPlatformQueryResult, error) 12 | // 查询指定数据 13 | Get(ctx context.Context, id string, opts ...schema.GPlatformQueryOptions) (*schema.GPlatform, error) 14 | // 查询select的选项 15 | GetOptions(ctx context.Context) (*schema.GPlatformSelectOptions, error) 16 | // 根据appId查询数据 17 | GetByAppId(ctx context.Context, appId string) (*schema.GPlatform, error) 18 | // 创建数据 19 | Create(ctx context.Context, item schema.GPlatform) error 20 | // 更新数据 21 | Update(ctx context.Context, id string, item schema.GPlatform) error 22 | // 删除数据 23 | Delete(ctx context.Context, id string) error 24 | // 更新状态 25 | UpdateStatus(ctx context.Context, id string, status int) error 26 | } 27 | -------------------------------------------------------------------------------- /internal/app/model/m_g_tpl_mall.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | type IGTplMall interface { 10 | // 查询数据 11 | Query(ctx context.Context, params schema.GTplMallQueryParam, opts ...schema.GTplMallQueryOptions) (*schema.GTplMallQueryResult, error) 12 | // 查询指定数据 13 | Get(ctx context.Context, id string, opts ...schema.GTplMallQueryOptions) (*schema.GTplMall, error) 14 | // 根据appId查询数据 15 | GetByIdentify(ctx context.Context, identify string) (*schema.GTplMall, error) 16 | // 创建数据 17 | Create(ctx context.Context, item schema.GTplMall) error 18 | // 更新数据 19 | Update(ctx context.Context, id string, item schema.GTplMall) error 20 | // 删除数据 21 | Delete(ctx context.Context, id string) error 22 | // 更新状态 23 | UpdateStatus(ctx context.Context, id string, status int) error 24 | } 25 | -------------------------------------------------------------------------------- /internal/app/model/m_menu.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IMenu 菜单管理存储接口 10 | type IMenu interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.MenuQueryParam, opts ...schema.MenuQueryOptions) (*schema.MenuQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.MenuQueryOptions) (*schema.Menu, error) 15 | // 根据路由查询对应的数据 16 | GetByRouter(ctx context.Context, router string) (*schema.Menu, error) 17 | // 创建数据 18 | Create(ctx context.Context, item schema.Menu) error 19 | // 更新数据 20 | Update(ctx context.Context, id string, item schema.Menu) error 21 | // 删除数据 22 | Delete(ctx context.Context, id string) error 23 | // 根据路由删除菜单 24 | DeleteByRouter(ctx context.Context, id string) error 25 | // 更新父级路径 26 | UpdateParentPath(ctx context.Context, id, parentPath string) error 27 | // 更新状态 28 | UpdateStatus(ctx context.Context, id string, status int) error 29 | } 30 | -------------------------------------------------------------------------------- /internal/app/model/m_menu_action.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IMenuAction 菜单动作管理存储接口 10 | type IMenuAction interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.MenuActionQueryParam, opts ...schema.MenuActionQueryOptions) (*schema.MenuActionQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.MenuActionQueryOptions) (*schema.MenuAction, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.MenuAction) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.MenuAction) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 根据菜单ID删除数据 22 | DeleteByMenuID(ctx context.Context, menuID string) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/model/m_menu_action_resource.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IMenuActionResource 菜单动作关联资源管理存储接口 10 | type IMenuActionResource interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.MenuActionResourceQueryParam, opts ...schema.MenuActionResourceQueryOptions) (*schema.MenuActionResourceQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.MenuActionResourceQueryOptions) (*schema.MenuActionResource, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.MenuActionResource) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.MenuActionResource) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 根据动作ID删除数据 22 | DeleteByActionID(ctx context.Context, actionID string) error 23 | // 根据菜单ID删除数据 24 | DeleteByMenuID(ctx context.Context, menuID string) error 25 | } 26 | -------------------------------------------------------------------------------- /internal/app/model/m_page_manager.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IPageManager 菜单管理存储接口 10 | type IPageManager interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.PageManagerQueryParam, opts ...schema.PageManagerQueryOptions) (*schema.PageManagerQueryResult, error) 13 | // 根据路由查询指定数据 14 | GetByRoute(ctx context.Context, id string, opts ...schema.PageManagerQueryOptions) (*schema.PageManager, error) 15 | // 查询指定数据 16 | Get(ctx context.Context, id string, opts ...schema.PageManagerQueryOptions) (*schema.PageManager, error) 17 | // 创建数据 18 | Create(ctx context.Context, item schema.PageManager) error 19 | // 更新数据 20 | Update(ctx context.Context, id string, item schema.PageManager) error 21 | // 删除数据 22 | Delete(ctx context.Context, id string) error 23 | // 更新状态 24 | UpdateStatus(ctx context.Context, id string, status int) error 25 | // 获取最后一条记录ID 26 | GetLastId(ctx context.Context) (int, error) 27 | } 28 | -------------------------------------------------------------------------------- /internal/app/model/m_page_version_history.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IPageVersionHistory 页面历史版本存储接口 10 | type IPageVersionHistory interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.PageVersionHistoryQueryParam, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistoryQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.PageVersionHistoryQueryOptions) (*schema.PageVersionHistory, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.PageVersionHistory) error 17 | // 删除数据 18 | Delete(ctx context.Context, id string) error 19 | } 20 | -------------------------------------------------------------------------------- /internal/app/model/m_role.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IRole 角色管理 10 | type IRole interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.RoleQueryParam, opts ...schema.RoleQueryOptions) (*schema.RoleQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.RoleQueryOptions) (*schema.Role, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.Role) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.Role) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 更新状态 22 | UpdateStatus(ctx context.Context, id string, status int) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/model/m_role_menu.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IRoleMenu 角色菜单存储接口 10 | type IRoleMenu interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.RoleMenuQueryParam, opts ...schema.RoleMenuQueryOptions) (*schema.RoleMenuQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.RoleMenuQueryOptions) (*schema.RoleMenu, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.RoleMenu) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.RoleMenu) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 根据角色ID删除数据 22 | DeleteByRoleID(ctx context.Context, roleID string) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/model/m_setting.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | type ISetting interface { 10 | // 查询数据 11 | Query(ctx context.Context) (*schema.SettingQueryResult, error) 12 | // 查询指定数据 13 | Get(ctx context.Context, id string) (*schema.Setting, error) 14 | // 创建数据 15 | Create(ctx context.Context, item schema.Setting) error 16 | // 更新数据 17 | Update(ctx context.Context, id string, item schema.Setting) error 18 | // 删除数据 19 | Delete(ctx context.Context, id string) error 20 | // 清空数据 21 | Truncate(ctx context.Context) error 22 | } 23 | -------------------------------------------------------------------------------- /internal/app/model/m_trans.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // ITrans 事务管理接口 8 | type ITrans interface { 9 | // 执行事务 10 | Exec(ctx context.Context, fn func(context.Context) error) error 11 | } 12 | -------------------------------------------------------------------------------- /internal/app/model/m_user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IUser 用户对象存储接口 10 | type IUser interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.UserQueryParam, opts ...schema.UserQueryOptions) (*schema.UserQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.UserQueryOptions) (*schema.User, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.User) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.User) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 更新状态 22 | UpdateStatus(ctx context.Context, id string, status int) error 23 | // 更新密码 24 | UpdatePassword(ctx context.Context, id, password string) error 25 | } 26 | -------------------------------------------------------------------------------- /internal/app/model/m_user_role.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 7 | ) 8 | 9 | // IUserRole 用户角色存储接口 10 | type IUserRole interface { 11 | // 查询数据 12 | Query(ctx context.Context, params schema.UserRoleQueryParam, opts ...schema.UserRoleQueryOptions) (*schema.UserRoleQueryResult, error) 13 | // 查询指定数据 14 | Get(ctx context.Context, id string, opts ...schema.UserRoleQueryOptions) (*schema.UserRole, error) 15 | // 创建数据 16 | Create(ctx context.Context, item schema.UserRole) error 17 | // 更新数据 18 | Update(ctx context.Context, id string, item schema.UserRole) error 19 | // 删除数据 20 | Delete(ctx context.Context, id string) error 21 | // 根据用户ID删除数据 22 | DeleteByUserID(ctx context.Context, userID string) error 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/casbin/casbin/v2" 5 | "github.com/gin-gonic/gin" 6 | "github.com/google/wire" 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/api" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth" 9 | ) 10 | 11 | var _ IRouter = (*Router)(nil) 12 | 13 | // RouterSet 注入router 14 | var RouterSet = wire.NewSet(wire.Struct(new(Router), "*"), wire.Bind(new(IRouter), new(*Router))) 15 | 16 | // IRouter 注册路由 17 | type IRouter interface { 18 | Register(app *gin.Engine) error 19 | Prefixes() []string 20 | } 21 | 22 | // Router 路由管理器 23 | type Router struct { 24 | Auth auth.Auther 25 | CasbinEnforcer *casbin.SyncedEnforcer 26 | DemoAPI *api.Demo 27 | LoginAPI *api.Login 28 | MenuAPI *api.Menu 29 | RoleAPI *api.Role 30 | UserAPI *api.User 31 | PageManagerAPI *api.PageManager 32 | PageVersionHistoryAPI *api.PageVersionHistory 33 | SettingAPI *api.Setting 34 | AppAPI *api.App 35 | GPlatFormAPI *api.GPlatform 36 | GTplMallAPI *api.GTplMall 37 | } 38 | 39 | // Register 注册路由 40 | func (a *Router) Register(app *gin.Engine) error { 41 | a.RegisterAPI(app) 42 | return nil 43 | } 44 | 45 | // Prefixes 路由前缀列表 46 | func (a *Router) Prefixes() []string { 47 | return []string{ 48 | "/api/", 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/app/schema/s_app.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | type AppQueryParam struct { 4 | AppId string `json:"app_id"` 5 | PlatformLogo string `json:"platform_logo"` 6 | PlatformName string `json:"platform_name"` 7 | } 8 | -------------------------------------------------------------------------------- /internal/app/schema/s_demo.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import "time" 4 | 5 | // Demo 示例对象 6 | type Demo struct { 7 | ID string `json:"id"` // 唯一标识 8 | Code string `json:"code" binding:"required"` // 编号 9 | Name string `json:"name" binding:"required"` // 名称 10 | Memo string `json:"memo"` // 备注 11 | Status int `json:"status" binding:"required,max=2,min=1"` // 状态(1:启用 2:停用) 12 | Creator string `json:"creator"` // 创建者 13 | CreatedAt time.Time `json:"created_at"` // 创建时间 14 | UpdatedAt time.Time `json:"updated_at"` // 更新时间 15 | } 16 | 17 | // DemoQueryParam 查询条件 18 | type DemoQueryParam struct { 19 | PaginationParam 20 | Code string `form:"-"` // 编号 21 | QueryValue string `form:"queryValue"` // 查询值 22 | } 23 | 24 | // DemoQueryOptions 示例对象查询可选参数项 25 | type DemoQueryOptions struct { 26 | OrderFields []*OrderField // 排序字段 27 | } 28 | 29 | // DemoQueryResult 示例对象查询结果 30 | type DemoQueryResult struct { 31 | Data []*Demo 32 | PageResult *PaginationResult 33 | } 34 | -------------------------------------------------------------------------------- /internal/app/schema/s_g_platform.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | type GPlatform struct { 4 | ID int `json:"id"` // 唯一标识 5 | AppID string `json:"app_id" binding:"required"` // 平台ID 6 | IsCurrent string `json:"is_current"` // 是否当前平台 7 | Name string `json:"name" binding:"required"` // 平台名称 8 | Status int `json:"status"` // 状态(1:启用 -1:停用) 9 | CreateTime int64 `json:"create_time"` // 创建时间 10 | } 11 | 12 | type GPlatformQueryParam struct { 13 | PaginationParam 14 | QueryValue string `form:"queryValue"` // 查询值 15 | } 16 | 17 | type GPlatformQueryOptions struct { 18 | OrderFields []*OrderField // 排序字段 19 | } 20 | 21 | type GPlatformQueryResult struct { 22 | Data []*GPlatform 23 | PageResult *PaginationResult 24 | } 25 | 26 | type Option struct { 27 | Label string `json:"label"` 28 | Value string `json:"value"` 29 | } 30 | 31 | type GPlatformSelectOptions struct { 32 | Options []*Option `json:"options"` 33 | } 34 | -------------------------------------------------------------------------------- /internal/app/schema/s_g_tpl_mall.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | type GTplMall struct { 4 | ID int `yaml:"-" json:"id"` // 自增ID 5 | Identify string `yaml:"identify" json:"identify"` // 模板标识 6 | Scope string `yaml:"scope" json:"scope"` // 应用限制 *为所有使用,其他情况为具体的app_id下才能可见 7 | Name string `yaml:"name" json:"name"` // 模板名abc称 8 | Desc string `yaml:"desc" json:"desc"` // 模板说明 9 | Meta string `yaml:"meta" json:"meta"` // 页面元信息 10 | MetaFormat string `yaml:"-" json:"meta_format"` // 页面元信息格式化 11 | Source string `yaml:"source" json:"source"` // 页面源码 12 | SourceFormat interface{} `yaml:"-" json:"source_format"` // 页面源码格式化 13 | MockData []MockDataItem `yaml:"mock_data" json:"mock_data"` // mock接口数据 14 | Icon string `yaml:"icon" json:"icon"` // 模板图标 15 | IconFull string `yaml:"-" json:"icon_full"` // 带i标签的模板图标 16 | Status int `yaml:"status" json:"status"` // 模板状态 17 | Creator string `yaml:"creator" json:"creator"` // 创建者 18 | CreateTime int64 `yaml:"-" json:"create_time"` // 创建时间 19 | UpdateTime int64 `yaml:"-" json:"update_time"` // 更新时间 20 | } 21 | 22 | type GTplMalls []*GTplMall 23 | 24 | type MockDataItem struct { 25 | Path string `json:"path"` 26 | Data string `json:"data"` 27 | } 28 | 29 | type GTplMallQueryParam struct { 30 | PaginationParam 31 | QueryValue string `form:"queryValue"` // 查询值 32 | AppId string 33 | UserId string 34 | } 35 | 36 | type GTplMallQueryOptions struct { 37 | OrderFields []*OrderField // 排序字段 38 | } 39 | 40 | type GTplMallQueryResult struct { 41 | Data []*GTplMall 42 | PageResult *PaginationResult 43 | } 44 | -------------------------------------------------------------------------------- /internal/app/schema/s_login.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | // LoginParam 登录参数 4 | type LoginParam struct { 5 | UserName string `json:"user_name" binding:"required"` // 用户名 6 | Password string `json:"password" binding:"required"` // 密码(md5加密) 7 | CaptchaID string `json:"captcha_id" binding:"required"` // 验证码ID 8 | CaptchaCode string `json:"captcha_code" binding:"required"` // 验证码 9 | } 10 | 11 | // UserLoginInfo 用户登录信息 12 | type UserLoginInfo struct { 13 | UserID string `json:"user_id"` // 用户ID 14 | UserName string `json:"user_name"` // 用户名 15 | RealName string `json:"real_name"` // 真实姓名 16 | Roles Roles `json:"roles"` // 角色列表 17 | } 18 | 19 | type Permissions []string 20 | 21 | // UpdatePasswordParam 更新密码请求参数 22 | type UpdatePasswordParam struct { 23 | OldPassword string `json:"old_password" binding:"required"` // 旧密码(md5加密) 24 | NewPassword string `json:"new_password" binding:"required"` // 新密码(md5加密) 25 | } 26 | 27 | // LoginCaptcha 登录验证码 28 | type LoginCaptcha struct { 29 | CaptchaID string `json:"captcha_id"` // 验证码ID 30 | } 31 | 32 | // LoginTokenInfo 登录令牌信息 33 | type LoginTokenInfo struct { 34 | AccessToken string `json:"access_token"` // 访问令牌 35 | TokenType string `json:"token_type"` // 令牌类型 36 | ExpiresAt int64 `json:"expires_at"` // 令牌到期时间戳 37 | } 38 | 39 | type SubjectInfo struct { 40 | UserID string `json:"user_id"` // 用户ID 41 | AppID string `json:"app_id"` // 应用ID 42 | } 43 | -------------------------------------------------------------------------------- /internal/app/schema/s_page_manager.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 5 | ) 6 | 7 | // PageManager 菜单对象 8 | type PageManager struct { 9 | ID int `yaml:"-" json:"id"` // 唯一标识 10 | Identify string `yaml:"identify" json:"identify"` // 页面标识 11 | Name string `yaml:"name" json:"name"` // 页面名称 12 | Meta string `yaml:"meta" json:"meta"` // 页面元信息 13 | Source string `yaml:"source" json:"source"` // 页面源码 14 | //OriginSource string `yaml:"-" json:"origin_source"` // 原始页面源码 15 | RenderSource string `yaml:"-" json:"render_source"` // 编译后页面源码 16 | Creator string `yaml:"creator" json:"creator"` // 创建者 17 | CreateTime int64 `yaml:"-" json:"create_time"` // 创建时间 18 | UpdateTime int64 `yaml:"-" json:"modify_time"` // 更新时间 19 | } 20 | 21 | type PageManagers []*PageManager 22 | 23 | func (a *PageManager) String() string { 24 | return util.JSONMarshalToString(a) 25 | } 26 | 27 | // PageManagerQueryParam 查询条件 28 | type PageManagerQueryParam struct { 29 | PaginationParam 30 | Route string `form:"route"` // 页面标识 31 | Name string `form:"queryValue"` // 页面名称 32 | } 33 | 34 | // PageManagerQueryOptions 查询可选参数项 35 | type PageManagerQueryOptions struct { 36 | OrderFields []*OrderField // 排序字段 37 | } 38 | 39 | // PageManagerQueryResult 查询结果 40 | type PageManagerQueryResult struct { 41 | Data PageManagers 42 | PageResult *PaginationResult 43 | } 44 | -------------------------------------------------------------------------------- /internal/app/schema/s_page_version_history.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 5 | ) 6 | 7 | // PageVersionHistory 菜单对象 8 | type PageVersionHistory struct { 9 | ID int `json:"id"` // 唯一标识 10 | Name string `json:"name"` // 页面名称 11 | PageManagerId int `json:"page_manager_id"` // 页面标识 12 | PageManagerInfo string `json:"page_manger_info"` // 页面历史信息 13 | CreateTime int64 `json:"history_create_time"` // 页面版本创建时间 14 | PageCreateTime int64 `json:"create_time"` // 页面创建时间 15 | } 16 | 17 | type PageVersionHistorys []*PageVersionHistory 18 | 19 | func (a *PageVersionHistory) String() string { 20 | return util.JSONMarshalToString(a) 21 | } 22 | 23 | // PageVersionHistoryQueryParam 查询条件 24 | type PageVersionHistoryQueryParam struct { 25 | PaginationParam 26 | PageManagerId string `form:"page_manager_id"` // 页面id 27 | } 28 | 29 | // PageVersionHistoryQueryOptions 查询可选参数项 30 | type PageVersionHistoryQueryOptions struct { 31 | OrderFields []*OrderField // 排序字段 32 | } 33 | 34 | // PageVersionHistoryQueryResult 查询结果 35 | type PageVersionHistoryQueryResult struct { 36 | Data PageVersionHistorys 37 | PageResult *PaginationResult 38 | } 39 | -------------------------------------------------------------------------------- /internal/app/schema/s_setting.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import "time" 4 | 5 | type Setting struct { 6 | Key string `json:"key" binding:"required"` // 键 7 | Value string `json:"value" binding:"required"` // 值 8 | CreatedAt time.Time `json:"created_at"` // 创建时间 9 | UpdatedAt time.Time `json:"updated_at"` // 更新时间 10 | } 11 | 12 | type SettingQueryResult struct { 13 | Data []*Setting 14 | } 15 | 16 | type Settings []*Setting 17 | 18 | type SettingBodyData struct { 19 | PlatformName string `json:"platform_name"` 20 | PlatformLogo string `json:"platform_logo"` 21 | GlobalEnv interface{} `json:"global_env"` 22 | DashboardRoute string `json:"dashboard_route"` 23 | } 24 | -------------------------------------------------------------------------------- /internal/app/swagger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package app 生成swagger文档 3 | 4 | 文档规则请参考:https://github.com/swaggo/swag#declarative-comments-format 5 | 6 | 使用方式: 7 | 8 | go get -u github.com/swaggo/swag/cmd/swag 9 | swag init --generalInfo ./internal/app/swagger.go --output ./internal/app/swagger */ 10 | package app 11 | 12 | // @title gin-admin 13 | // @version 6.4.2 14 | // @description RBAC scaffolding based on GIN + GORM/MONGO + CASBIN + WIRE. 15 | // @securityDefinitions.apikey ApiKeyAuth 16 | // @in header 17 | // @name Authorization 18 | // @schemes http https 19 | // @basePath / 20 | // @contact.name LyricTian 21 | // @contact.email tiannianshou@gmail.com 22 | -------------------------------------------------------------------------------- /internal/app/test/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package test 接口测试 3 | 4 | 使用方式: 5 | 6 | go test -v 7 | */ 8 | package test 9 | -------------------------------------------------------------------------------- /internal/app/test/t_demo_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http/httptest" 5 | "testing" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/unique" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestDemo(t *testing.T) { 13 | const router = apiPrefix + "v1/demos" 14 | var err error 15 | 16 | w := httptest.NewRecorder() 17 | 18 | // post /demos 19 | addItem := &schema.Demo{ 20 | Code: unique.MustUUID().String(), 21 | Name: unique.MustUUID().String(), 22 | Status: 1, 23 | } 24 | engine.ServeHTTP(w, newPostRequest(router, addItem)) 25 | assert.Equal(t, 200, w.Code) 26 | var addItemRes ResID 27 | err = parseReader(w.Body, &addItemRes) 28 | assert.Nil(t, err) 29 | 30 | // get /demos/:id 31 | engine.ServeHTTP(w, newGetRequest("%s/%s", nil, router, addItemRes.ID)) 32 | assert.Equal(t, 200, w.Code) 33 | var getItem schema.Demo 34 | err = parseReader(w.Body, &getItem) 35 | assert.Nil(t, err) 36 | assert.Equal(t, addItem.Code, getItem.Code) 37 | assert.Equal(t, addItem.Name, getItem.Name) 38 | assert.Equal(t, addItem.Status, getItem.Status) 39 | assert.NotEmpty(t, getItem.ID) 40 | 41 | // put /demos/:id 42 | putItem := getItem 43 | putItem.Name = unique.MustUUID().String() 44 | engine.ServeHTTP(w, newPutRequest("%s/%s", putItem, router, getItem.ID)) 45 | assert.Equal(t, 200, w.Code) 46 | err = parseOK(w.Body) 47 | assert.Nil(t, err) 48 | 49 | // query /demos 50 | engine.ServeHTTP(w, newGetRequest(router, newPageParam())) 51 | assert.Equal(t, 200, w.Code) 52 | var pageItems []*schema.Demo 53 | err = parsePageReader(w.Body, &pageItems) 54 | assert.Nil(t, err) 55 | assert.GreaterOrEqual(t, len(pageItems), 1) 56 | if len(pageItems) > 0 { 57 | assert.Equal(t, putItem.ID, pageItems[0].ID) 58 | assert.Equal(t, putItem.Name, pageItems[0].Name) 59 | } 60 | 61 | // delete /demos/:id 62 | engine.ServeHTTP(w, newDeleteRequest("%s/%s", router, addItemRes.ID)) 63 | assert.Equal(t, 200, w.Code) 64 | err = parseOK(w.Body) 65 | assert.Nil(t, err) 66 | } 67 | -------------------------------------------------------------------------------- /internal/app/test/t_menu_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http/httptest" 5 | "testing" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/unique" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestMenu(t *testing.T) { 13 | const router = apiPrefix + "v1/menus" 14 | var err error 15 | 16 | w := httptest.NewRecorder() 17 | 18 | // post /menus 19 | addItem := &schema.Menu{ 20 | Name: unique.MustUUID().String(), 21 | ShowStatus: 1, 22 | Status: 1, 23 | } 24 | engine.ServeHTTP(w, newPostRequest(router, addItem)) 25 | assert.Equal(t, 200, w.Code) 26 | var addItemRes ResID 27 | err = parseReader(w.Body, &addItemRes) 28 | assert.Nil(t, err) 29 | 30 | // get /menus/:id 31 | engine.ServeHTTP(w, newGetRequest("%s/%s", nil, router, addItemRes.ID)) 32 | assert.Equal(t, 200, w.Code) 33 | var getItem schema.Menu 34 | err = parseReader(w.Body, &getItem) 35 | assert.Nil(t, err) 36 | assert.Equal(t, addItem.Name, getItem.Name) 37 | assert.Equal(t, addItem.Status, getItem.Status) 38 | assert.NotEmpty(t, getItem.ID) 39 | 40 | // put /menus/:id 41 | putItem := getItem 42 | putItem.Name = unique.MustUUID().String() 43 | engine.ServeHTTP(w, newPutRequest("%s/%s", putItem, router, getItem.ID)) 44 | assert.Equal(t, 200, w.Code) 45 | err = parseOK(w.Body) 46 | assert.Nil(t, err) 47 | 48 | // query /menus 49 | engine.ServeHTTP(w, newGetRequest(router, newPageParam())) 50 | assert.Equal(t, 200, w.Code) 51 | var pageItems []*schema.Menu 52 | err = parsePageReader(w.Body, &pageItems) 53 | assert.Nil(t, err) 54 | assert.GreaterOrEqual(t, len(pageItems), 1) 55 | if len(pageItems) > 0 { 56 | assert.Equal(t, putItem.ID, pageItems[0].ID) 57 | assert.Equal(t, putItem.Name, pageItems[0].Name) 58 | } 59 | 60 | // delete /menus/:id 61 | engine.ServeHTTP(w, newDeleteRequest("%s/%s", router, addItemRes.ID)) 62 | assert.Equal(t, 200, w.Code) 63 | err = parseOK(w.Body) 64 | assert.Nil(t, err) 65 | } 66 | -------------------------------------------------------------------------------- /internal/app/test/t_role_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http/httptest" 5 | "testing" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/internal/app/schema" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/unique" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestRole(t *testing.T) { 13 | const router = apiPrefix + "v1/roles" 14 | var err error 15 | 16 | w := httptest.NewRecorder() 17 | 18 | // post /menus 19 | addMenuItem := &schema.Menu{ 20 | Name: unique.MustUUID().String(), 21 | ShowStatus: 1, 22 | Status: 1, 23 | } 24 | engine.ServeHTTP(w, newPostRequest(apiPrefix+"v1/menus", addMenuItem)) 25 | assert.Equal(t, 200, w.Code) 26 | var addMenuItemRes ResID 27 | err = parseReader(w.Body, &addMenuItemRes) 28 | assert.Nil(t, err) 29 | 30 | // post /roles 31 | addItem := &schema.Role{ 32 | Name: unique.MustUUID().String(), 33 | Status: 1, 34 | RoleMenus: schema.RoleMenus{ 35 | &schema.RoleMenu{ 36 | MenuID: addMenuItemRes.ID, 37 | }, 38 | }, 39 | } 40 | engine.ServeHTTP(w, newPostRequest(router, addItem)) 41 | assert.Equal(t, 200, w.Code) 42 | var addItemRes ResID 43 | err = parseReader(w.Body, &addItemRes) 44 | assert.Nil(t, err) 45 | 46 | // get /roles/:id 47 | engine.ServeHTTP(w, newGetRequest("%s/%s", nil, router, addItemRes.ID)) 48 | assert.Equal(t, 200, w.Code) 49 | var getItem schema.Role 50 | err = parseReader(w.Body, &getItem) 51 | assert.Nil(t, err) 52 | assert.Equal(t, addItem.Name, getItem.Name) 53 | assert.Equal(t, addItem.Status, getItem.Status) 54 | assert.NotEmpty(t, getItem.ID) 55 | 56 | // put /roles/:id 57 | putItem := getItem 58 | putItem.Name = unique.MustUUID().String() 59 | engine.ServeHTTP(w, newPutRequest("%s/%s", putItem, router, getItem.ID)) 60 | assert.Equal(t, 200, w.Code) 61 | err = parseOK(w.Body) 62 | assert.Nil(t, err) 63 | 64 | // query /roles 65 | engine.ServeHTTP(w, newGetRequest(router, newPageParam())) 66 | assert.Equal(t, 200, w.Code) 67 | var pageItems []*schema.Role 68 | err = parsePageReader(w.Body, &pageItems) 69 | assert.Nil(t, err) 70 | assert.GreaterOrEqual(t, len(pageItems), 1) 71 | if len(pageItems) > 0 { 72 | assert.Equal(t, putItem.ID, pageItems[0].ID) 73 | assert.Equal(t, putItem.Name, pageItems[0].Name) 74 | } 75 | 76 | // delete /roles/:id 77 | engine.ServeHTTP(w, newDeleteRequest("%s/%s", router, addItemRes.ID)) 78 | assert.Equal(t, 200, w.Code) 79 | err = parseOK(w.Body) 80 | assert.Nil(t, err) 81 | 82 | // delete /menus/:id 83 | engine.ServeHTTP(w, newDeleteRequest(apiPrefix+"v1/menus/%s", addMenuItemRes.ID)) 84 | assert.Equal(t, 200, w.Code) 85 | err = parseOK(w.Body) 86 | assert.Nil(t, err) 87 | } 88 | -------------------------------------------------------------------------------- /pkg/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | // 定义错误 9 | var ( 10 | ErrInvalidToken = errors.New("invalid token") 11 | ) 12 | 13 | // TokenInfo 令牌信息 14 | type TokenInfo interface { 15 | // 获取访问令牌 16 | GetAccessToken() string 17 | // 获取令牌类型 18 | GetTokenType() string 19 | // 获取令牌到期时间戳 20 | GetExpiresAt() int64 21 | // JSON编码 22 | EncodeToJSON() ([]byte, error) 23 | } 24 | 25 | // Auther 认证接口 26 | type Auther interface { 27 | // 生成令牌 28 | GenerateToken(ctx context.Context, userID string) (TokenInfo, error) 29 | 30 | // 销毁令牌 31 | DestroyToken(ctx context.Context, accessToken string) error 32 | 33 | // 解析用户ID 34 | ParseUserID(ctx context.Context, accessToken string) (string, error) 35 | 36 | // 释放资源 37 | Release() error 38 | } 39 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/auth_test.go: -------------------------------------------------------------------------------- 1 | package jwtauth 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/auth/jwtauth/store/buntdb" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestAuth(t *testing.T) { 12 | store, err := buntdb.NewStore(":memory:") 13 | assert.Nil(t, err) 14 | 15 | jwtAuth := New(store) 16 | 17 | defer jwtAuth.Release() 18 | 19 | ctx := context.Background() 20 | userID := "test" 21 | token, err := jwtAuth.GenerateToken(ctx, userID) 22 | assert.Nil(t, err) 23 | assert.NotNil(t, token) 24 | 25 | id, err := jwtAuth.ParseUserID(ctx, token.GetAccessToken()) 26 | assert.Nil(t, err) 27 | assert.Equal(t, userID, id) 28 | 29 | err = jwtAuth.DestroyToken(ctx, token.GetAccessToken()) 30 | assert.Nil(t, err) 31 | 32 | id, err = jwtAuth.ParseUserID(ctx, token.GetAccessToken()) 33 | assert.NotNil(t, err) 34 | assert.EqualError(t, err, "invalid token") 35 | assert.Empty(t, id) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/store.go: -------------------------------------------------------------------------------- 1 | package jwtauth 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // Storer 令牌存储接口 9 | type Storer interface { 10 | // 存储令牌数据,并指定到期时间 11 | Set(ctx context.Context, tokenString string, expiration time.Duration) error 12 | // 检查令牌是否存在 13 | Check(ctx context.Context, tokenString string) (bool, error) 14 | // 关闭存储 15 | Close() error 16 | } 17 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/store/buntdb/buntdb.go: -------------------------------------------------------------------------------- 1 | package buntdb 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | 9 | "github.com/tidwall/buntdb" 10 | ) 11 | 12 | // NewStore 创建基于buntdb的文件存储 13 | func NewStore(path string) (*Store, error) { 14 | if path != ":memory:" { 15 | os.MkdirAll(filepath.Dir(path), 0777) 16 | } 17 | 18 | db, err := buntdb.Open(path) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return &Store{ 24 | db: db, 25 | }, nil 26 | } 27 | 28 | // Store buntdb存储 29 | type Store struct { 30 | db *buntdb.DB 31 | } 32 | 33 | // Set ... 34 | func (a *Store) Set(ctx context.Context, tokenString string, expiration time.Duration) error { 35 | return a.db.Update(func(tx *buntdb.Tx) error { 36 | var opts *buntdb.SetOptions 37 | if expiration > 0 { 38 | opts = &buntdb.SetOptions{Expires: true, TTL: expiration} 39 | } 40 | _, _, err := tx.Set(tokenString, "1", opts) 41 | return err 42 | }) 43 | } 44 | 45 | // Delete 删除键 46 | func (a *Store) Delete(ctx context.Context, tokenString string) error { 47 | return a.db.Update(func(tx *buntdb.Tx) error { 48 | _, err := tx.Delete(tokenString) 49 | if err != nil && err != buntdb.ErrNotFound { 50 | return err 51 | } 52 | return nil 53 | }) 54 | } 55 | 56 | // Check ... 57 | func (a *Store) Check(ctx context.Context, tokenString string) (bool, error) { 58 | var exists bool 59 | err := a.db.View(func(tx *buntdb.Tx) error { 60 | val, err := tx.Get(tokenString) 61 | if err != nil && err != buntdb.ErrNotFound { 62 | return err 63 | } 64 | exists = val == "1" 65 | return nil 66 | }) 67 | return exists, err 68 | } 69 | 70 | // Close ... 71 | func (a *Store) Close() error { 72 | return a.db.Close() 73 | } 74 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/store/buntdb/buntdb_test.go: -------------------------------------------------------------------------------- 1 | package buntdb 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestStore(t *testing.T) { 11 | store, err := NewStore(":memory:") 12 | assert.Nil(t, err) 13 | 14 | defer store.Close() 15 | 16 | key := "test" 17 | ctx := context.Background() 18 | err = store.Set(ctx, key, 0) 19 | assert.Nil(t, err) 20 | 21 | b, err := store.Check(ctx, key) 22 | assert.Nil(t, err) 23 | assert.Equal(t, true, b) 24 | 25 | err = store.Delete(ctx, key) 26 | assert.Nil(t, err) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/store/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/go-redis/redis" 9 | ) 10 | 11 | // Config redis配置参数 12 | type Config struct { 13 | Addr string // 地址(IP:Port) 14 | DB int // 数据库 15 | Password string // 密码 16 | KeyPrefix string // 存储key的前缀 17 | } 18 | 19 | // NewStore 创建基于redis存储实例 20 | func NewStore(cfg *Config) *Store { 21 | cli := redis.NewClient(&redis.Options{ 22 | Addr: cfg.Addr, 23 | DB: cfg.DB, 24 | Password: cfg.Password, 25 | }) 26 | return &Store{ 27 | cli: cli, 28 | prefix: cfg.KeyPrefix, 29 | } 30 | } 31 | 32 | // NewStoreWithClient 使用redis客户端创建存储实例 33 | func NewStoreWithClient(cli *redis.Client, keyPrefix string) *Store { 34 | return &Store{ 35 | cli: cli, 36 | prefix: keyPrefix, 37 | } 38 | } 39 | 40 | // NewStoreWithClusterClient 使用redis集群客户端创建存储实例 41 | func NewStoreWithClusterClient(cli *redis.ClusterClient, keyPrefix string) *Store { 42 | return &Store{ 43 | cli: cli, 44 | prefix: keyPrefix, 45 | } 46 | } 47 | 48 | type redisClienter interface { 49 | Get(key string) *redis.StringCmd 50 | Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd 51 | Expire(key string, expiration time.Duration) *redis.BoolCmd 52 | Exists(keys ...string) *redis.IntCmd 53 | TxPipeline() redis.Pipeliner 54 | Del(keys ...string) *redis.IntCmd 55 | Close() error 56 | } 57 | 58 | // Store redis存储 59 | type Store struct { 60 | cli redisClienter 61 | prefix string 62 | } 63 | 64 | func (s *Store) wrapperKey(key string) string { 65 | return fmt.Sprintf("%s%s", s.prefix, key) 66 | } 67 | 68 | // Set ... 69 | func (s *Store) Set(ctx context.Context, tokenString string, expiration time.Duration) error { 70 | cmd := s.cli.Set(s.wrapperKey(tokenString), "1", expiration) 71 | return cmd.Err() 72 | } 73 | 74 | // Delete ... 75 | func (s *Store) Delete(ctx context.Context, tokenString string) error { 76 | cmd := s.cli.Del(tokenString) 77 | if err := cmd.Err(); err != nil { 78 | return err 79 | } 80 | return nil 81 | } 82 | 83 | // Check ... 84 | func (s *Store) Check(ctx context.Context, tokenString string) (bool, error) { 85 | cmd := s.cli.Exists(s.wrapperKey(tokenString)) 86 | if err := cmd.Err(); err != nil { 87 | return false, err 88 | } 89 | return cmd.Val() > 0, nil 90 | } 91 | 92 | // Close ... 93 | func (s *Store) Close() error { 94 | return s.cli.Close() 95 | } 96 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/store/redis/redis_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | const ( 11 | addr = "127.0.0.1:6379" 12 | ) 13 | 14 | func TestStore(t *testing.T) { 15 | store := NewStore(&Config{ 16 | Addr: addr, 17 | DB: 1, 18 | }) 19 | 20 | defer store.Close() 21 | 22 | key := "test" 23 | ctx := context.Background() 24 | err := store.Set(ctx, key, 0) 25 | assert.Nil(t, err) 26 | 27 | b, err := store.Check(ctx, key) 28 | assert.Nil(t, err) 29 | assert.Equal(t, true, b) 30 | 31 | err = store.Delete(ctx, key) 32 | assert.Nil(t, err) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/auth/jwtauth/token.go: -------------------------------------------------------------------------------- 1 | package jwtauth 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // tokenInfo 令牌信息 8 | type tokenInfo struct { 9 | AccessToken string `json:"access_token"` // 访问令牌 10 | TokenType string `json:"token_type"` // 令牌类型 11 | ExpiresAt int64 `json:"expires_at"` // 令牌到期时间 12 | } 13 | 14 | func (t *tokenInfo) GetAccessToken() string { 15 | return t.AccessToken 16 | } 17 | 18 | func (t *tokenInfo) GetTokenType() string { 19 | return t.TokenType 20 | } 21 | 22 | func (t *tokenInfo) GetExpiresAt() int64 { 23 | return t.ExpiresAt 24 | } 25 | 26 | func (t *tokenInfo) EncodeToJSON() ([]byte, error) { 27 | return json.Marshal(t) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | // 定义别名 8 | var ( 9 | New = errors.New 10 | Wrap = errors.Wrap 11 | Wrapf = errors.Wrapf 12 | WithStack = errors.WithStack 13 | WithMessage = errors.WithMessage 14 | WithMessagef = errors.WithMessagef 15 | ) 16 | 17 | // 定义错误 18 | var ( 19 | ErrBadRequest = New400Response("请求发生错误") 20 | ErrInvalidParent = New400Response("无效的父级节点") 21 | ErrNotAllowDeleteWithChild = New400Response("含有子级,不能删除") 22 | ErrNotAllowDelete = New400Response("资源不允许删除") 23 | ErrNotAllowUpdate = New400Response("资源不允许更新") 24 | ErrInvalidUserName = New400Response("无效的用户名") 25 | ErrInvalidPassword = New400Response("无效的密码") 26 | ErrInvalidUser = New400Response("无效的用户") 27 | ErrUserDisable = New400Response("用户被禁用,请联系管理员") 28 | 29 | ErrNoPerm = NewResponse(401, 401, "无访问权限") 30 | ErrInvalidToken = NewResponse(9999, 401, "令牌失效") 31 | ErrNotFound = NewResponse(404, 404, "资源不存在") 32 | ErrMethodNotAllow = NewResponse(405, 405, "方法不被允许") 33 | ErrTooManyRequests = NewResponse(429, 429, "请求过于频繁") 34 | ErrInternalServer = NewResponse(500, 500, "服务器发生错误") 35 | ) 36 | -------------------------------------------------------------------------------- /pkg/errors/response.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import "fmt" 4 | 5 | // ResponseError 定义响应错误 6 | type ResponseError struct { 7 | Code int // 错误码 8 | Message string // 错误消息 9 | StatusCode int // 响应状态码 10 | ERR error // 响应错误 11 | } 12 | 13 | func (r *ResponseError) Error() string { 14 | if r.ERR != nil { 15 | return r.ERR.Error() 16 | } 17 | return r.Message 18 | } 19 | 20 | // UnWrapResponse 解包响应错误 21 | func UnWrapResponse(err error) *ResponseError { 22 | if v, ok := err.(*ResponseError); ok { 23 | return v 24 | } 25 | return nil 26 | } 27 | 28 | // WrapResponse 包装响应错误 29 | func WrapResponse(err error, code, statusCode int, msg string, args ...interface{}) error { 30 | res := &ResponseError{ 31 | Code: code, 32 | Message: fmt.Sprintf(msg, args...), 33 | ERR: err, 34 | StatusCode: statusCode, 35 | } 36 | return res 37 | } 38 | 39 | // Wrap400Response 包装错误码为400的响应错误 40 | func Wrap400Response(err error, msg string, args ...interface{}) error { 41 | return WrapResponse(err, 400, 400, msg, args...) 42 | } 43 | 44 | // Wrap500Response 包装错误码为500的响应错误 45 | func Wrap500Response(err error, msg string, args ...interface{}) error { 46 | return WrapResponse(err, 500, 500, msg, args...) 47 | } 48 | 49 | // NewResponse 创建响应错误 50 | func NewResponse(code, statusCode int, msg string, args ...interface{}) error { 51 | res := &ResponseError{ 52 | Code: code, 53 | Message: fmt.Sprintf(msg, args...), 54 | StatusCode: statusCode, 55 | } 56 | return res 57 | } 58 | 59 | // New400Response 创建错误码为400的响应错误 60 | func New400Response(msg string, args ...interface{}) error { 61 | return NewResponse(400, 400, msg, args...) 62 | } 63 | 64 | // New500Response 创建错误码为500的响应错误 65 | func New500Response(msg string, args ...interface{}) error { 66 | return NewResponse(500, 500, msg, args...) 67 | } 68 | -------------------------------------------------------------------------------- /pkg/logger/hook/gorm/gorm.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 8 | "github.com/jinzhu/gorm" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | var tableName string 13 | 14 | // Config 配置参数 15 | type Config struct { 16 | DBType string 17 | DSN string 18 | MaxLifetime int 19 | MaxOpenConns int 20 | MaxIdleConns int 21 | TableName string 22 | } 23 | 24 | // New 创建基于gorm的钩子实例(需要指定表名) 25 | func New(c *Config) *Hook { 26 | tableName = c.TableName 27 | 28 | db, err := gorm.Open(c.DBType, c.DSN) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | db.DB().SetMaxIdleConns(c.MaxIdleConns) 34 | db.DB().SetMaxOpenConns(c.MaxOpenConns) 35 | db.DB().SetConnMaxLifetime(time.Duration(c.MaxLifetime) * time.Second) 36 | 37 | db.AutoMigrate(new(LogItem)) 38 | return &Hook{ 39 | db: db, 40 | } 41 | } 42 | 43 | // Hook gorm日志钩子 44 | type Hook struct { 45 | db *gorm.DB 46 | } 47 | 48 | // Exec 执行日志写入 49 | func (h *Hook) Exec(entry *logrus.Entry) error { 50 | item := &LogItem{ 51 | Level: entry.Level.String(), 52 | Message: entry.Message, 53 | CreatedAt: entry.Time, 54 | } 55 | 56 | data := entry.Data 57 | if v, ok := data[logger.TraceIDKey]; ok { 58 | item.TraceID, _ = v.(string) 59 | delete(data, logger.TraceIDKey) 60 | } 61 | if v, ok := data[logger.UserIDKey]; ok { 62 | item.UserID, _ = v.(string) 63 | delete(data, logger.UserIDKey) 64 | } 65 | if v, ok := data[logger.SpanTitleKey]; ok { 66 | item.SpanTitle, _ = v.(string) 67 | delete(data, logger.SpanTitleKey) 68 | } 69 | if v, ok := data[logger.SpanFunctionKey]; ok { 70 | item.SpanFunction, _ = v.(string) 71 | delete(data, logger.SpanFunctionKey) 72 | } 73 | if v, ok := data[logger.VersionKey]; ok { 74 | item.Version, _ = v.(string) 75 | delete(data, logger.VersionKey) 76 | } 77 | 78 | if len(data) > 0 { 79 | item.Data = util.JSONMarshalToString(data) 80 | } 81 | 82 | result := h.db.Create(item) 83 | if err := result.Error; err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | 89 | // Close 关闭钩子 90 | func (h *Hook) Close() error { 91 | return h.db.Close() 92 | } 93 | 94 | // LogItem 存储日志项 95 | type LogItem struct { 96 | ID uint `gorm:"column:id;primary_key;auto_increment;"` // id 97 | Level string `gorm:"column:level;size:20;index;"` // 日志级别 98 | Message string `gorm:"column:message;size:1024;"` // 消息 99 | TraceID string `gorm:"column:trace_id;size:128;index;"` // 跟踪ID 100 | UserID string `gorm:"column:user_id;size:36;index;"` // 用户ID 101 | SpanTitle string `gorm:"column:span_title;size:256;"` // 跟踪单元标题 102 | SpanFunction string `gorm:"column:span_function;size:256;"` // 跟踪单元函数名 103 | Data string `gorm:"column:data;type:text;"` // 日志数据(json) 104 | Version string `gorm:"column:version;index;size:32;"` // 服务版本号 105 | CreatedAt time.Time `gorm:"column:created_at;index"` // 创建时间 106 | } 107 | 108 | // TableName 表名 109 | func (LogItem) TableName() string { 110 | return tableName 111 | } 112 | -------------------------------------------------------------------------------- /pkg/logger/hook/mongo/mongo.go: -------------------------------------------------------------------------------- 1 | package bson 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/tanjiancheng/gin-amis-admin/pkg/logger" 8 | "github.com/tanjiancheng/gin-amis-admin/pkg/util" 9 | "github.com/sirupsen/logrus" 10 | "go.mongodb.org/mongo-driver/bson/primitive" 11 | "go.mongodb.org/mongo-driver/mongo" 12 | "go.mongodb.org/mongo-driver/mongo/options" 13 | ) 14 | 15 | // Config 配置参数 16 | type Config struct { 17 | URI string 18 | Database string 19 | Collection string 20 | Timeout time.Duration 21 | } 22 | 23 | func handleError(err error) { 24 | if err != nil { 25 | panic(err) 26 | } 27 | } 28 | 29 | // New 创建基于bson的钩子实例(需要指定表名) 30 | func New(cfg *Config) *Hook { 31 | var ( 32 | ctx = context.Background() 33 | cancel context.CancelFunc 34 | ) 35 | 36 | if t := cfg.Timeout; t > 0 { 37 | ctx, cancel = context.WithTimeout(ctx, t) 38 | defer cancel() 39 | } 40 | 41 | cli, err := mongo.Connect(ctx, options.Client().ApplyURI(cfg.URI)) 42 | handleError(err) 43 | c := cli.Database(cfg.Database).Collection(cfg.Collection) 44 | 45 | return &Hook{ 46 | Client: cli, 47 | Collection: c, 48 | } 49 | } 50 | 51 | // Hook bson日志钩子 52 | type Hook struct { 53 | Client *mongo.Client 54 | Collection *mongo.Collection 55 | } 56 | 57 | // Exec 执行日志写入 58 | func (h *Hook) Exec(entry *logrus.Entry) error { 59 | item := &LogItem{ 60 | ID: primitive.NewObjectID().Hex(), 61 | Level: entry.Level.String(), 62 | Message: entry.Message, 63 | CreatedAt: entry.Time, 64 | } 65 | 66 | data := entry.Data 67 | if v, ok := data[logger.TraceIDKey]; ok { 68 | item.TraceID, _ = v.(string) 69 | delete(data, logger.TraceIDKey) 70 | } 71 | if v, ok := data[logger.UserIDKey]; ok { 72 | item.UserID, _ = v.(string) 73 | delete(data, logger.UserIDKey) 74 | } 75 | if v, ok := data[logger.SpanTitleKey]; ok { 76 | item.SpanTitle, _ = v.(string) 77 | delete(data, logger.SpanTitleKey) 78 | } 79 | if v, ok := data[logger.SpanFunctionKey]; ok { 80 | item.SpanFunction, _ = v.(string) 81 | delete(data, logger.SpanFunctionKey) 82 | } 83 | if v, ok := data[logger.VersionKey]; ok { 84 | item.Version, _ = v.(string) 85 | delete(data, logger.VersionKey) 86 | } 87 | 88 | if len(data) > 0 { 89 | item.Data = util.JSONMarshalToString(data) 90 | } 91 | 92 | _, err := h.Collection.InsertOne(context.Background(), item) 93 | return err 94 | } 95 | 96 | // Close 关闭钩子 97 | func (h *Hook) Close() error { 98 | return h.Client.Disconnect(context.Background()) 99 | } 100 | 101 | // LogItem 存储日志项 102 | type LogItem struct { 103 | ID string `bson:"_id"` // id 104 | Level string `bson:"level"` // 日志级别 105 | Message string `bson:"message"` // 消息 106 | TraceID string `bson:"trace_id"` // 跟踪ID 107 | UserID string `bson:"user_id"` // 用户ID 108 | SpanTitle string `bson:"span_title"` // 跟踪单元标题 109 | SpanFunction string `bson:"span_function"` // 跟踪单元函数名 110 | Data string `bson:"data"` // 日志数据(json) 111 | Version string `bson:"version"` // 服务版本号 112 | CreatedAt time.Time `bson:"created_at"` // 创建时间 113 | } 114 | -------------------------------------------------------------------------------- /pkg/trace/trace.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | // IDFunc Get trace id 10 | type IDFunc func() string 11 | 12 | var ( 13 | idFunc IDFunc 14 | pid = os.Getpid() 15 | ) 16 | 17 | func init() { 18 | idFunc = func() string { 19 | return fmt.Sprintf("trace-id-%d-%s", 20 | pid, 21 | time.Now().Format("2006.01.02.15.04.05.999999")) 22 | } 23 | } 24 | 25 | // SetIDFunc Set trace id func 26 | func SetIDFunc(traceIDFunc IDFunc) { 27 | idFunc = traceIDFunc 28 | } 29 | 30 | // NewID Create trace id 31 | func NewID() string { 32 | return idFunc() 33 | } 34 | -------------------------------------------------------------------------------- /pkg/unique/object_id.go: -------------------------------------------------------------------------------- 1 | package unique 2 | 3 | import ( 4 | "go.mongodb.org/mongo-driver/bson/primitive" 5 | ) 6 | 7 | // ObjectID Define alias 8 | type ObjectID = primitive.ObjectID 9 | 10 | // NewObjectID Create object id 11 | func NewObjectID() ObjectID { 12 | return primitive.NewObjectID() 13 | } 14 | -------------------------------------------------------------------------------- /pkg/unique/snowflake.go: -------------------------------------------------------------------------------- 1 | package unique 2 | 3 | import ( 4 | "github.com/bwmarrin/snowflake" 5 | ) 6 | 7 | // snowflakeNode Define snowflake node (default 1) 8 | var snowflakeNode *snowflake.Node 9 | 10 | func init() { 11 | node, err := snowflake.NewNode(1) 12 | if err != nil { 13 | panic(err) 14 | } 15 | snowflakeNode = node 16 | } 17 | 18 | // SetSnowflakeNode Set snowflake node 19 | func SetSnowflakeNode(node, epoch int64) error { 20 | if epoch > 0 { 21 | snowflake.Epoch = epoch 22 | } 23 | 24 | n, err := snowflake.NewNode(node) 25 | if err != nil { 26 | return err 27 | } 28 | snowflakeNode = n 29 | return nil 30 | } 31 | 32 | // SnowflakeID Define alias 33 | type SnowflakeID = snowflake.ID 34 | 35 | // NewSnowflakeID Create snowflake id 36 | func NewSnowflakeID() SnowflakeID { 37 | return snowflakeNode.Generate() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/unique/uuid.go: -------------------------------------------------------------------------------- 1 | package unique 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | ) 6 | 7 | // UUID Define alias 8 | type UUID = uuid.UUID 9 | 10 | // NewUUID Create uuid 11 | func NewUUID() (UUID, error) { 12 | return uuid.NewRandom() 13 | } 14 | 15 | // MustUUID Create uuid(Throw panic if something goes wrong) 16 | func MustUUID() UUID { 17 | v, err := NewUUID() 18 | if err != nil { 19 | panic(err) 20 | } 21 | return v 22 | } 23 | -------------------------------------------------------------------------------- /pkg/util/hash.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/sha1" 6 | "fmt" 7 | ) 8 | 9 | // MD5Hash MD5哈希值 10 | func MD5Hash(b []byte) string { 11 | h := md5.New() 12 | _, _ = h.Write(b) 13 | return fmt.Sprintf("%x", h.Sum(nil)) 14 | } 15 | 16 | // MD5HashString MD5哈希值 17 | func MD5HashString(s string) string { 18 | return MD5Hash([]byte(s)) 19 | } 20 | 21 | // SHA1Hash SHA1哈希值 22 | func SHA1Hash(b []byte) string { 23 | h := sha1.New() 24 | _, _ = h.Write(b) 25 | return fmt.Sprintf("%x", h.Sum(nil)) 26 | } 27 | 28 | // SHA1HashString SHA1哈希值 29 | func SHA1HashString(s string) string { 30 | return SHA1Hash([]byte(s)) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/util/json.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | jsoniter "github.com/json-iterator/go" 5 | ) 6 | 7 | // 定义JSON操作 8 | var ( 9 | json = jsoniter.ConfigCompatibleWithStandardLibrary 10 | JSONMarshal = json.Marshal 11 | JSONUnmarshal = json.Unmarshal 12 | JSONMarshalIndent = json.MarshalIndent 13 | JSONNewDecoder = json.NewDecoder 14 | JSONNewEncoder = json.NewEncoder 15 | ) 16 | 17 | // JSONMarshalToString JSON编码为字符串 18 | func JSONMarshalToString(v interface{}) string { 19 | s, err := jsoniter.MarshalToString(v) 20 | if err != nil { 21 | return "" 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /pkg/util/string.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // S 字符串类型转换 8 | type S string 9 | 10 | func (s S) String() string { 11 | return string(s) 12 | } 13 | 14 | // Bytes 转换为[]byte 15 | func (s S) Bytes() []byte { 16 | return []byte(s) 17 | } 18 | 19 | // Bool 转换为bool 20 | func (s S) Bool() (bool, error) { 21 | b, err := strconv.ParseBool(s.String()) 22 | if err != nil { 23 | return false, err 24 | } 25 | return b, nil 26 | } 27 | 28 | // DefaultBool 转换为bool,如果出现错误则使用默认值 29 | func (s S) DefaultBool(defaultVal bool) bool { 30 | b, err := s.Bool() 31 | if err != nil { 32 | return defaultVal 33 | } 34 | return b 35 | } 36 | 37 | // Int64 转换为int64 38 | func (s S) Int64() (int64, error) { 39 | i, err := strconv.ParseInt(s.String(), 10, 64) 40 | if err != nil { 41 | return 0, err 42 | } 43 | return i, nil 44 | } 45 | 46 | // DefaultInt64 转换为int64,如果出现错误则使用默认值 47 | func (s S) DefaultInt64(defaultVal int64) int64 { 48 | i, err := s.Int64() 49 | if err != nil { 50 | return defaultVal 51 | } 52 | return i 53 | } 54 | 55 | // Int 转换为int 56 | func (s S) Int() (int, error) { 57 | i, err := s.Int64() 58 | if err != nil { 59 | return 0, err 60 | } 61 | return int(i), nil 62 | } 63 | 64 | // DefaultInt 转换为int,如果出现错误则使用默认值 65 | func (s S) DefaultInt(defaultVal int) int { 66 | i, err := s.Int() 67 | if err != nil { 68 | return defaultVal 69 | } 70 | return i 71 | } 72 | 73 | // Uint64 转换为uint64 74 | func (s S) Uint64() (uint64, error) { 75 | i, err := strconv.ParseUint(s.String(), 10, 64) 76 | if err != nil { 77 | return 0, err 78 | } 79 | return i, nil 80 | } 81 | 82 | // DefaultUint64 转换为uint64,如果出现错误则使用默认值 83 | func (s S) DefaultUint64(defaultVal uint64) uint64 { 84 | i, err := s.Uint64() 85 | if err != nil { 86 | return defaultVal 87 | } 88 | return i 89 | } 90 | 91 | // Uint 转换为uint 92 | func (s S) Uint() (uint, error) { 93 | i, err := s.Uint64() 94 | if err != nil { 95 | return 0, err 96 | } 97 | return uint(i), nil 98 | } 99 | 100 | // DefaultUint 转换为uint,如果出现错误则使用默认值 101 | func (s S) DefaultUint(defaultVal uint) uint { 102 | i, err := s.Uint() 103 | if err != nil { 104 | return defaultVal 105 | } 106 | return uint(i) 107 | } 108 | 109 | // Float64 转换为float64 110 | func (s S) Float64() (float64, error) { 111 | f, err := strconv.ParseFloat(s.String(), 64) 112 | if err != nil { 113 | return 0, err 114 | } 115 | return f, nil 116 | } 117 | 118 | // DefaultFloat64 转换为float64,如果出现错误则使用默认值 119 | func (s S) DefaultFloat64(defaultVal float64) float64 { 120 | f, err := s.Float64() 121 | if err != nil { 122 | return defaultVal 123 | } 124 | return f 125 | } 126 | 127 | // Float32 转换为float32 128 | func (s S) Float32() (float32, error) { 129 | f, err := s.Float64() 130 | if err != nil { 131 | return 0, err 132 | } 133 | return float32(f), nil 134 | } 135 | 136 | // DefaultFloat32 转换为float32,如果出现错误则使用默认值 137 | func (s S) DefaultFloat32(defaultVal float32) float32 { 138 | f, err := s.Float32() 139 | if err != nil { 140 | return defaultVal 141 | } 142 | return f 143 | } 144 | 145 | // ToJSON 转换为JSON 146 | func (s S) ToJSON(v interface{}) error { 147 | return json.Unmarshal(s.Bytes(), v) 148 | } 149 | -------------------------------------------------------------------------------- /pkg/util/string_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestS(t *testing.T) { 10 | intS := S("1010") 11 | i, err := intS.Int64() 12 | assert.Nil(t, err) 13 | assert.EqualValues(t, 1010, i) 14 | 15 | floatS := S("10.1") 16 | f, err := floatS.Float64() 17 | assert.Nil(t, err) 18 | assert.EqualValues(t, 10.1, f) 19 | 20 | di := floatS.DefaultInt64(5) 21 | assert.EqualValues(t, 5, di) 22 | 23 | boolS := S("true") 24 | b, err := boolS.Bool() 25 | assert.Nil(t, err) 26 | assert.EqualValues(t, true, b) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/jinzhu/copier" 5 | ) 6 | 7 | // StructMapToStruct 结构体映射 8 | func StructMapToStruct(s, ts interface{}) error { 9 | return copier.Copy(ts, s) 10 | } 11 | -------------------------------------------------------------------------------- /pkg/util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestStructMapToStruct(t *testing.T) { 11 | type Foo struct { 12 | SBar *string 13 | FBar float64 14 | IBar int64 15 | TBar time.Time 16 | PBar *string 17 | CreatedAt time.Time 18 | } 19 | 20 | type NFoo struct { 21 | CreatedAt time.Time 22 | } 23 | 24 | type TFoo struct { 25 | NFoo 26 | ID int64 27 | SBar string 28 | FBar *float64 29 | IBar *int64 30 | TBar *time.Time 31 | PBar string 32 | } 33 | 34 | bar := "bar" 35 | var foo = Foo{ 36 | SBar: &bar, 37 | FBar: 1.1, 38 | IBar: 1, 39 | TBar: time.Now(), 40 | CreatedAt: time.Now(), 41 | } 42 | 43 | var tfoo TFoo 44 | StructMapToStruct(&foo, &tfoo) 45 | 46 | assert.Equal(t, *foo.SBar, tfoo.SBar) 47 | assert.Equal(t, foo.FBar, *tfoo.FBar) 48 | assert.Equal(t, foo.IBar, *tfoo.IBar) 49 | assert.Equal(t, foo.TBar, *tfoo.TBar) 50 | assert.Equal(t, foo.CreatedAt, tfoo.CreatedAt) 51 | assert.Equal(t, tfoo.PBar, "") 52 | } 53 | -------------------------------------------------------------------------------- /pkg/util/yaml.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | ) 6 | 7 | // 定义YAML操作 8 | var ( 9 | YAMLMarshal = yaml.Marshal 10 | YAMLUnmarshal = yaml.Unmarshal 11 | YAMLNewDecoder = yaml.NewDecoder 12 | YAMLNewEncoder = yaml.NewEncoder 13 | ) 14 | -------------------------------------------------------------------------------- /scripts/init_mysql.sql: -------------------------------------------------------------------------------- 1 | -- Create a database 2 | CREATE DATABASE `gin-admin` DEFAULT CHARACTER SET = `utf8mb4`; -------------------------------------------------------------------------------- /scripts/init_postgres.sql: -------------------------------------------------------------------------------- 1 | -- Create a database 2 | create database gin-admin with encoding = 'UTF8' LC_CTYPE = 'en_US.UTF-8' LC_COLLATE = 'en_US.UTF-8' template = template1; -------------------------------------------------------------------------------- /scripts/pack/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: start build 2 | 3 | NOW = $(shell date -u '+%Y%m%d%I%M%S') 4 | 5 | APP = gin-amis-admin 6 | SERVER_BIN = ${APP} 7 | 8 | all: start 9 | 10 | start: 11 | ./${SERVER_BIN} web -c ./configs/config.toml -m ./configs/model.conf --menu ./configs/menu.yaml --page ./configs/page_manager.yaml --tpl-mall ./configs/tpl_mall.yaml 12 | -------------------------------------------------------------------------------- /scripts/pack/README.md: -------------------------------------------------------------------------------- 1 | ## 启动程序 2 | > linux/drawin 3 | ``` 4 | make start 5 | ``` 6 | > windows 7 | ``` 8 | 双击start.bat 9 | ``` 10 | 11 | 12 | ## 访问地址 13 | ``` 14 | http://127.0.0.1:10088 15 | 用户名: root 16 | 密码: abc-123 17 | ``` 18 | > 服务相关配置可以查看configs/config.toml自行根据需要修改 19 | > 页面相关配置可以查看web/js/config.js自行根据需要修改 20 | 21 | 22 | -------------------------------------------------------------------------------- /scripts/pack/start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | path = %path%;.; 3 | gin-amis-admin.exe web -c ./configs/config.toml -m ./configs/model.conf --menu ./configs/menu.yaml --page ./configs/page_manager.yaml --tpl-mall ./configs/tpl_mall.yaml 4 | pause 5 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | /public 2 | /package-lock.json 3 | /gh-pages -------------------------------------------------------------------------------- /web/css/login.css: -------------------------------------------------------------------------------- 1 | html,body { 2 | height: 100%; 3 | } 4 | 5 | body.my-login-page { 6 | background-color: #f7f9fb; 7 | font-size: 14px; 8 | } 9 | 10 | .my-login-page .brand { 11 | width: 90px; 12 | height: 90px; 13 | overflow: hidden; 14 | border-radius: 50%; 15 | margin: 40px auto; 16 | box-shadow: 0 4px 8px rgba(0,0,0,.05); 17 | position: relative; 18 | z-index: 1; 19 | } 20 | 21 | .my-login-page .brand img { 22 | width: 100%; 23 | } 24 | 25 | .my-login-page .card-wrapper { 26 | width: 400px; 27 | } 28 | 29 | .my-login-page .card { 30 | border-color: transparent; 31 | box-shadow: 0 4px 8px rgba(0,0,0,.05); 32 | } 33 | 34 | .my-login-page .card.fat { 35 | padding: 10px; 36 | } 37 | 38 | .my-login-page .card .card-title { 39 | margin-bottom: 30px; 40 | } 41 | 42 | /*.my-login-page .form-control { 43 | border-width: 2.3px; 44 | }*/ 45 | 46 | .my-login-page .form-group label { 47 | width: 100%; 48 | } 49 | 50 | .my-login-page .btn.btn-block { 51 | padding: 12px 10px; 52 | } 53 | 54 | .my-login-page .footer { 55 | margin: 40px 0; 56 | color: #888; 57 | text-align: center; 58 | } 59 | 60 | @media screen and (max-width: 425px) { 61 | .my-login-page .card-wrapper { 62 | width: 90%; 63 | margin: 0 auto; 64 | } 65 | } 66 | 67 | @media screen and (max-width: 320px) { 68 | .my-login-page .card.fat { 69 | padding: 0; 70 | } 71 | 72 | .my-login-page .card.fat .card-body { 73 | padding: 15px; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /web/css/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .app-wrapper { 10 | position: relative; 11 | width: 100%; 12 | height: 100%; 13 | } 14 | 15 | .navbar-nav .navbar-btn { 16 | padding: 8px; 17 | } 18 | 19 | .history-version-modal { 20 | z-index: 1100 !important; 21 | } 22 | 23 | .a-Modal--2th { 24 | z-index: 1400 !important; 25 | } 26 | 27 | .a-Modal--3th { 28 | z-index: 1500 !important; 29 | } 30 | 31 | .toolbar { 32 | position: relative; 33 | top: -20px; 34 | } 35 | 36 | #toolbar .user-name { 37 | border-color: white; 38 | box-shadow: unset; 39 | } 40 | 41 | #toolbar .user-name:hover { 42 | border-color: white; 43 | box-shadow: unset; 44 | background-color: white; 45 | } 46 | 47 | .view-page-source { 48 | position: relative; 49 | top: -58px; 50 | left: 30px; 51 | border-color: white !important; 52 | box-shadow: unset !important; 53 | } 54 | 55 | .goto-github { 56 | position: relative; 57 | top: -57px; 58 | left: 26px; 59 | border-color: white !important; 60 | box-shadow: unset !important; 61 | } 62 | 63 | .goto-github a { 64 | color: unset !important; 65 | } 66 | 67 | .float-right { 68 | float: right; 69 | } 70 | 71 | /*.editor-full .monaco-editor { 72 | height: 750px !important; 73 | }*/ 74 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/favicon.ico -------------------------------------------------------------------------------- /web/img/guide/page-add.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/img/guide/page-add.jpg -------------------------------------------------------------------------------- /web/img/guide/permission-button-visible.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/img/guide/permission-button-visible.jpg -------------------------------------------------------------------------------- /web/img/guide/push-menu-first.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/img/guide/push-menu-first.jpg -------------------------------------------------------------------------------- /web/img/guide/push-menu-result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/img/guide/push-menu-result.jpg -------------------------------------------------------------------------------- /web/img/guide/push-menu-second.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/img/guide/push-menu-second.jpg -------------------------------------------------------------------------------- /web/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanjiancheng/gin-amis-admin/4f5e48f487cb305fe41685aaa68eead231195a9e/web/img/logo.jpg -------------------------------------------------------------------------------- /web/js/common.js: -------------------------------------------------------------------------------- 1 | function getUrlParam(name) { 2 | let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); 3 | let r = window.location.search.substr(1).match(reg); 4 | if (r != null) return unescape(r[2]); 5 | return null; 6 | }; 7 | 8 | function getSetting() { 9 | let setting = {}; 10 | $.ajax({ 11 | async: false, 12 | url: pageSchemaApi + '/api/v1/pub/setting', 13 | type: "GET", 14 | dataType: "json", //指定服务器返回的数据类型 15 | beforeSend: function (request) { 16 | request.setRequestHeader("X-App-Id", getAppId()); 17 | request.setRequestHeader("authorization", getAuthorization()); 18 | }, 19 | success: function (response) { 20 | setting = response.data || {}; 21 | } 22 | }); 23 | return setting; 24 | } 25 | 26 | function setAuthorization(auth) { 27 | return store.session("auth", auth); 28 | } 29 | 30 | function getAuthorization() { 31 | let authorization = store.session("auth"); 32 | let authorizationToken = ''; 33 | if (authorization != undefined && authorization.access_token != undefined) { 34 | authorizationToken = authorization.token_type + " " + authorization.access_token; 35 | } 36 | return authorizationToken; 37 | } 38 | 39 | function getAppId() { 40 | let appId = getUrlParam("app_id"); 41 | if (appId == undefined || appId.length <= 0 || appId === null) { 42 | appId = store.session("app_id"); 43 | } 44 | if (appId == undefined || appId.length <= 0 || appId === null) { 45 | appId = defaultAppId 46 | } 47 | return appId 48 | } 49 | 50 | function setAppId(appId) { 51 | return store.session("app_id", appId); 52 | } -------------------------------------------------------------------------------- /web/js/config.js: -------------------------------------------------------------------------------- 1 | //获取页面json格式接口api 2 | const defaultAppId = "default"; 3 | const pageSchemaApi = "http://127.0.0.1:10088"; 4 | 5 | -------------------------------------------------------------------------------- /web/js/preload.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | //判断应用是否初始化 3 | function checkAppInit() { 4 | $.ajax({ 5 | async: true, 6 | url: pageSchemaApi + '/api/v1/pub/app/' + getAppId(), 7 | type: "GET", 8 | dataType: "json", //指定服务器返回的数据类型 9 | //cache: false, 10 | beforeSend: function (request) { 11 | request.setRequestHeader("authorization", getAuthorization()); 12 | }, 13 | success: function (response) { 14 | let status = response.status; 15 | if (response.status == undefined) { 16 | status = -1 17 | } 18 | let isInitApp = response.data; 19 | if (response.data === undefined) { 20 | isInitApp = true; 21 | } 22 | if (status == 0 && !isInitApp) { 23 | window.location.href = "/page/wizard.html"; 24 | } 25 | } 26 | }) 27 | } 28 | checkAppInit() 29 | })(); -------------------------------------------------------------------------------- /web/js/wizard.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let amis = amisRequire("amis/embed"); 3 | let oldAppId = store.session("app_id"); 4 | let appId = getAppId(); 5 | setAppId(appId); 6 | amis.embed("#wizard", { 7 | "$schema": "https://houtai.baidu.com/v2/schemas/page.json#", 8 | "title": [ 9 | "应用初始化向导", 10 | { 11 | "type": "button", 12 | "label": "取消并返回", 13 | "className": "float-right", 14 | "level": "danger", 15 | "actionType": "link", 16 | "link": "/?app_id="+oldAppId, 17 | } 18 | ], 19 | "body": [ 20 | 21 | { 22 | "type": "wizard", 23 | "api": { 24 | "url": pageSchemaApi + "/api/v1/app", 25 | "method": "post", 26 | "headers": { 27 | "Authorization": getAuthorization() 28 | }, 29 | }, 30 | "redirect": "/#/dashboard", 31 | "steps": [ 32 | { 33 | "title": "配置平台信息", 34 | "controls": [ 35 | { 36 | "label": "应用ID", 37 | "name": "app_id", 38 | "type": "static", 39 | "size": "lg", 40 | "value": appId 41 | }, 42 | { 43 | "label": "平台名称", 44 | "name": "platform_name", 45 | "type": "text", 46 | "size": "lg", 47 | "hint": "为你的后台起一个好听的名字吧" 48 | }, 49 | { 50 | "label": "平台logo", 51 | "name": "platform_logo", 52 | "placeholder": "fa fa-fort-awesome", 53 | "value": "fa fa-fort-awesome", 54 | "remark": "fontawesome图标", 55 | "size": "lg", 56 | "type": "text" 57 | }, 58 | ] 59 | }, 60 | { 61 | "title": "初始化并创建", 62 | "controls": [ 63 | "

请确定以下信息:


", 64 | "

应用ID: ${app_id}


", 65 | "

平台名称: ${platform_name}


", 66 | "

平台logo:


", 67 | "

注:相关平台信息可以不填,如果后续有修改,可以进入平台后在系统管理/系统配置里更改

", 68 | ] 69 | } 70 | ] 71 | } 72 | ] 73 | }) 74 | })(); -------------------------------------------------------------------------------- /web/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 后台登录 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 |
18 | logo 19 |
20 |
21 |
22 |

后台登录

23 | 66 |
67 |
68 | 71 |
72 |
73 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /web/page/wizard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 应用初始化 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------