From 54135d2be565a1159c2015c940fb58fba85400da Mon Sep 17 00:00:00 2001 From: xpcool Date: Fri, 17 Jul 2026 17:58:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(dev):=20=E9=A1=B9=E7=9B=AE=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 1 + .gitignore | 19 +++ Makefile | 7 + PROJECT_STRUCTURE.md | 27 ++++ README.MD | 22 +++ api/admin/v1/auth.go | 15 ++ api/hello/hello.go | 15 ++ api/hello/v1/hello.go | 12 ++ api/user/v1/auth.go | 25 +++ go.mod | 36 +++++ go.sum | 83 ++++++++++ hack/config.yaml | 13 ++ hack/hack-cli.mk | 18 +++ hack/hack.mk | 75 +++++++++ internal/cmd/cmd.go | 54 +++++++ internal/consts/consts.go | 1 + internal/consts/error_code.go | 15 ++ internal/controller/admin/auth.go | 19 +++ internal/controller/hello/hello.go | 5 + internal/controller/hello/hello_new.go | 15 ++ internal/controller/hello/hello_v1_hello.go | 13 ++ internal/controller/user/auth.go | 26 +++ internal/dao/.gitkeep | 0 internal/dao/admin_menu.go | 22 +++ internal/dao/admin_operation_log.go | 22 +++ internal/dao/admin_role.go | 22 +++ internal/dao/admin_role_menu.go | 22 +++ internal/dao/admin_user.go | 22 +++ internal/dao/admin_user_role.go | 22 +++ internal/dao/auth_refresh_session.go | 22 +++ internal/dao/content.go | 22 +++ internal/dao/internal/admin_menu.go | 99 ++++++++++++ internal/dao/internal/admin_operation_log.go | 101 ++++++++++++ internal/dao/internal/admin_role.go | 91 +++++++++++ internal/dao/internal/admin_role_menu.go | 89 ++++++++++ internal/dao/internal/admin_user.go | 95 +++++++++++ internal/dao/internal/admin_user_role.go | 89 ++++++++++ internal/dao/internal/auth_refresh_session.go | 97 +++++++++++ internal/dao/internal/content.go | 91 +++++++++++ internal/dao/internal/user.go | 103 ++++++++++++ internal/dao/internal/user_favorite.go | 89 ++++++++++ internal/dao/internal/user_message.go | 93 +++++++++++ internal/dao/user.go | 22 +++ internal/dao/user_favorite.go | 22 +++ internal/dao/user_message.go | 22 +++ internal/library/jwt/jwt.go | 92 +++++++++++ internal/library/page/page.go | 13 ++ internal/library/response/error.go | 11 ++ internal/library/response/response.go | 15 ++ internal/middleware/auth.go | 71 ++++++++ internal/middleware/common.go | 26 +++ internal/model/.gitkeep | 0 internal/model/do/.gitkeep | 0 internal/model/do/admin_menu.go | 26 +++ internal/model/do/admin_operation_log.go | 27 ++++ internal/model/do/admin_role.go | 22 +++ internal/model/do/admin_role_menu.go | 21 +++ internal/model/do/admin_user.go | 24 +++ internal/model/do/admin_user_role.go | 21 +++ internal/model/do/auth_refresh_session.go | 25 +++ internal/model/do/content.go | 22 +++ internal/model/do/user.go | 28 ++++ internal/model/do/user_favorite.go | 21 +++ internal/model/do/user_message.go | 23 +++ internal/model/dto/auth.go | 9 ++ internal/model/entity/.gitkeep | 0 internal/model/entity/admin_menu.go | 24 +++ internal/model/entity/admin_operation_log.go | 25 +++ internal/model/entity/admin_role.go | 20 +++ internal/model/entity/admin_role_menu.go | 19 +++ internal/model/entity/admin_user.go | 22 +++ internal/model/entity/admin_user_role.go | 19 +++ internal/model/entity/auth_refresh_session.go | 23 +++ internal/model/entity/content.go | 20 +++ internal/model/entity/user.go | 26 +++ internal/model/entity/user_favorite.go | 19 +++ internal/model/entity/user_message.go | 21 +++ internal/model/vo/auth.go | 9 ++ internal/service/.gitkeep | 0 internal/service/admin_audit.go | 20 +++ internal/service/admin_auth.go | 44 +++++ internal/service/auth.go | 51 ++++++ internal/service/user_auth.go | 131 +++++++++++++++ internal/table/admin_menu.go | 133 +++++++++++++++ internal/table/admin_operation_log.go | 143 ++++++++++++++++ internal/table/admin_role.go | 93 +++++++++++ internal/table/admin_role_menu.go | 83 ++++++++++ internal/table/admin_user.go | 113 +++++++++++++ internal/table/admin_user_role.go | 83 ++++++++++ internal/table/auth_refresh_session.go | 123 ++++++++++++++ internal/table/content.go | 93 +++++++++++ internal/table/user.go | 153 ++++++++++++++++++ internal/table/user_favorite.go | 83 ++++++++++ internal/table/user_message.go | 103 ++++++++++++ main.go | 11 ++ manifest/config/config.dev.yaml | 13 ++ manifest/config/config.prod.yaml | 4 + manifest/config/config.test.yaml | 3 + .../deploy/kustomize/base/deployment.yaml | 21 +++ .../deploy/kustomize/base/kustomization.yaml | 8 + manifest/deploy/kustomize/base/service.yaml | 12 ++ .../kustomize/overlays/develop/configmap.yaml | 14 ++ .../overlays/develop/deployment.yaml | 10 ++ .../overlays/develop/kustomization.yaml | 14 ++ manifest/docker/Dockerfile | 16 ++ manifest/docker/docker.sh | 8 + manifest/i18n/.gitkeep | 0 manifest/protobuf/.keep-if-necessary | 0 manifest/sql/001_core.sql | 29 ++++ manifest/sql/002_business_extension.sql | 52 ++++++ resource/public/html/.gitkeep | 0 resource/public/plugin/.gitkeep | 0 resource/public/resource/css/.gitkeep | 0 resource/public/resource/image/.gitkeep | 0 resource/public/resource/js/.gitkeep | 0 resource/template/.gitkeep | 0 utility/.gitkeep | 0 117 files changed, 4203 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 PROJECT_STRUCTURE.md create mode 100644 README.MD create mode 100644 api/admin/v1/auth.go create mode 100644 api/hello/hello.go create mode 100644 api/hello/v1/hello.go create mode 100644 api/user/v1/auth.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 hack/config.yaml create mode 100644 hack/hack-cli.mk create mode 100644 hack/hack.mk create mode 100644 internal/cmd/cmd.go create mode 100644 internal/consts/consts.go create mode 100644 internal/consts/error_code.go create mode 100644 internal/controller/admin/auth.go create mode 100644 internal/controller/hello/hello.go create mode 100644 internal/controller/hello/hello_new.go create mode 100644 internal/controller/hello/hello_v1_hello.go create mode 100644 internal/controller/user/auth.go create mode 100644 internal/dao/.gitkeep create mode 100644 internal/dao/admin_menu.go create mode 100644 internal/dao/admin_operation_log.go create mode 100644 internal/dao/admin_role.go create mode 100644 internal/dao/admin_role_menu.go create mode 100644 internal/dao/admin_user.go create mode 100644 internal/dao/admin_user_role.go create mode 100644 internal/dao/auth_refresh_session.go create mode 100644 internal/dao/content.go create mode 100644 internal/dao/internal/admin_menu.go create mode 100644 internal/dao/internal/admin_operation_log.go create mode 100644 internal/dao/internal/admin_role.go create mode 100644 internal/dao/internal/admin_role_menu.go create mode 100644 internal/dao/internal/admin_user.go create mode 100644 internal/dao/internal/admin_user_role.go create mode 100644 internal/dao/internal/auth_refresh_session.go create mode 100644 internal/dao/internal/content.go create mode 100644 internal/dao/internal/user.go create mode 100644 internal/dao/internal/user_favorite.go create mode 100644 internal/dao/internal/user_message.go create mode 100644 internal/dao/user.go create mode 100644 internal/dao/user_favorite.go create mode 100644 internal/dao/user_message.go create mode 100644 internal/library/jwt/jwt.go create mode 100644 internal/library/page/page.go create mode 100644 internal/library/response/error.go create mode 100644 internal/library/response/response.go create mode 100644 internal/middleware/auth.go create mode 100644 internal/middleware/common.go create mode 100644 internal/model/.gitkeep create mode 100644 internal/model/do/.gitkeep create mode 100644 internal/model/do/admin_menu.go create mode 100644 internal/model/do/admin_operation_log.go create mode 100644 internal/model/do/admin_role.go create mode 100644 internal/model/do/admin_role_menu.go create mode 100644 internal/model/do/admin_user.go create mode 100644 internal/model/do/admin_user_role.go create mode 100644 internal/model/do/auth_refresh_session.go create mode 100644 internal/model/do/content.go create mode 100644 internal/model/do/user.go create mode 100644 internal/model/do/user_favorite.go create mode 100644 internal/model/do/user_message.go create mode 100644 internal/model/dto/auth.go create mode 100644 internal/model/entity/.gitkeep create mode 100644 internal/model/entity/admin_menu.go create mode 100644 internal/model/entity/admin_operation_log.go create mode 100644 internal/model/entity/admin_role.go create mode 100644 internal/model/entity/admin_role_menu.go create mode 100644 internal/model/entity/admin_user.go create mode 100644 internal/model/entity/admin_user_role.go create mode 100644 internal/model/entity/auth_refresh_session.go create mode 100644 internal/model/entity/content.go create mode 100644 internal/model/entity/user.go create mode 100644 internal/model/entity/user_favorite.go create mode 100644 internal/model/entity/user_message.go create mode 100644 internal/model/vo/auth.go create mode 100644 internal/service/.gitkeep create mode 100644 internal/service/admin_audit.go create mode 100644 internal/service/admin_auth.go create mode 100644 internal/service/auth.go create mode 100644 internal/service/user_auth.go create mode 100644 internal/table/admin_menu.go create mode 100644 internal/table/admin_operation_log.go create mode 100644 internal/table/admin_role.go create mode 100644 internal/table/admin_role_menu.go create mode 100644 internal/table/admin_user.go create mode 100644 internal/table/admin_user_role.go create mode 100644 internal/table/auth_refresh_session.go create mode 100644 internal/table/content.go create mode 100644 internal/table/user.go create mode 100644 internal/table/user_favorite.go create mode 100644 internal/table/user_message.go create mode 100644 main.go create mode 100644 manifest/config/config.dev.yaml create mode 100644 manifest/config/config.prod.yaml create mode 100644 manifest/config/config.test.yaml create mode 100644 manifest/deploy/kustomize/base/deployment.yaml create mode 100644 manifest/deploy/kustomize/base/kustomization.yaml create mode 100644 manifest/deploy/kustomize/base/service.yaml create mode 100644 manifest/deploy/kustomize/overlays/develop/configmap.yaml create mode 100644 manifest/deploy/kustomize/overlays/develop/deployment.yaml create mode 100644 manifest/deploy/kustomize/overlays/develop/kustomization.yaml create mode 100644 manifest/docker/Dockerfile create mode 100644 manifest/docker/docker.sh create mode 100644 manifest/i18n/.gitkeep create mode 100644 manifest/protobuf/.keep-if-necessary create mode 100644 manifest/sql/001_core.sql create mode 100644 manifest/sql/002_business_extension.sql create mode 100644 resource/public/html/.gitkeep create mode 100644 resource/public/plugin/.gitkeep create mode 100644 resource/public/resource/css/.gitkeep create mode 100644 resource/public/resource/image/.gitkeep create mode 100644 resource/public/resource/js/.gitkeep create mode 100644 resource/template/.gitkeep create mode 100644 utility/.gitkeep diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1fbf887 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* linguist-language=GO \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..18646b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +.buildpath +.hgignore.swp +.project +.orig +.swp +.idea/ +.settings/ +.vscode/ +bin/ +**/.DS_Store +gf +main +main.exe +output/ +manifest/output/ +temp/ +temp.yaml +bin +**/config/config.yaml \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2a6e6e9 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +ROOT_DIR = $(shell pwd) +NAMESPACE = "default" +DEPLOY_NAME = "template-single" +DOCKER_NAME = "template-single" + +include ./hack/hack-cli.mk +include ./hack/hack.mk \ No newline at end of file diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..cbee06a --- /dev/null +++ b/PROJECT_STRUCTURE.md @@ -0,0 +1,27 @@ +# Production layout + +```text +service.xpcool.com/ +├── api/ # HTTP contracts and Swagger metadata +│ ├── user/v1/ # /api/v1 - client-facing API +│ └── admin/v1/ # /admin/v1 - administration API +├── internal/ +│ ├── cmd/ # application bootstrap and route isolation +│ ├── consts/ # application error codes and constants +│ ├── controller/ # API-to-service adapters only +│ ├── service/ # domain use cases and provider interfaces +│ ├── dao/ # generated by gf gen dao; never hand edited +│ ├── model/ +│ │ ├── entity/ # generated database entities +│ │ ├── do/ # generated Data Objects +│ │ ├── dto/ # service boundary input/output +│ │ └── vo/ # API view models +│ ├── middleware/ # configurable route-group middleware +│ └── library/ # JWT, response, pagination primitives +├── manifest/ +│ ├── config/ # config.dev/test/prod.yaml +│ └── sql/ # ordered MySQL migrations +└── utility/ # optional cross-cutting helpers +``` + +`dao`, `model/do` and `model/entity` are generated after migration, so their schema never drifts from MySQL. Controllers do not access DAO; only services do. diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..17bf26f --- /dev/null +++ b/README.MD @@ -0,0 +1,22 @@ +# Personal multi-client service + +## Layout + +`api → internal/controller → internal/service → internal/dao → internal/model(entity/do)` is enforced. DTO and VO live in `internal/model/dto` and `internal/model/vo`; neither API types nor entities cross that boundary. + +## Database model generation + +Run `manifest/sql/001_core.sql` on MySQL, set `DB_DSN`, then run: + +```powershell +gf gen dao -p internal -g default -gt -c +``` + +The command is intentionally the only source of `internal/dao`, `internal/model/do`, and `internal/model/entity`. + +## API isolation + +- `/api/v1/*`: user API, terminal header/body supports `mini`, `h5`, and `app`. +- `/admin/v1/*`: admin API. Protected endpoints require both an admin access token and an `X-Permission` identifier. + +Use `GF_GCFG_FILE=config.dev.yaml` (or `config.test.yaml` / `config.prod.yaml`) and set `DB_DSN` plus a strong `JWT_SECRET` before startup. diff --git a/api/admin/v1/auth.go b/api/admin/v1/auth.go new file mode 100644 index 0000000..5428d2f --- /dev/null +++ b/api/admin/v1/auth.go @@ -0,0 +1,15 @@ +package v1 + +import "github.com/gogf/gf/v2/frame/g" + +type LoginReq struct { + g.Meta `path:"/auth/login" method:"post" tags:"Admin/Auth" summary:"Administrator login"` + Username string `json:"username" v:"required"` + Password string `json:"password" v:"required"` +} +type LoginRes struct { + AccessToken string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int64 `json:"expiresIn"` + AdminID uint64 `json:"adminId"` +} diff --git a/api/hello/hello.go b/api/hello/hello.go new file mode 100644 index 0000000..a9b48fa --- /dev/null +++ b/api/hello/hello.go @@ -0,0 +1,15 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package hello + +import ( + "context" + + "service.xpcool.com/api/hello/v1" +) + +type IHelloV1 interface { + Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) +} diff --git a/api/hello/v1/hello.go b/api/hello/v1/hello.go new file mode 100644 index 0000000..b4dd233 --- /dev/null +++ b/api/hello/v1/hello.go @@ -0,0 +1,12 @@ +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +type HelloReq struct { + g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"` +} +type HelloRes struct { + g.Meta `mime:"text/html" example:"string"` +} diff --git a/api/user/v1/auth.go b/api/user/v1/auth.go new file mode 100644 index 0000000..04064f6 --- /dev/null +++ b/api/user/v1/auth.go @@ -0,0 +1,25 @@ +package v1 + +import "github.com/gogf/gf/v2/frame/g" + +type LoginReq struct { + g.Meta `path:"/auth/login" method:"post" tags:"User/Auth" summary:"User login"` + LoginType string `json:"loginType" v:"required|in:wechat,mobile,password#login type required|unsupported login type"` + Code string `json:"code"` + Mobile string `json:"mobile"` + VerifyCode string `json:"verifyCode"` + Account string `json:"account"` + Password string `json:"password"` + Terminal string `json:"terminal" v:"required|in:mini,h5,app#terminal required|invalid terminal"` +} +type LoginRes struct { + AccessToken string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int64 `json:"expiresIn"` + UserID uint64 `json:"userId"` +} +type RefreshReq struct { + g.Meta `path:"/auth/refresh" method:"post" tags:"User/Auth" summary:"Rotate user token"` + RefreshToken string `json:"refreshToken" v:"required"` +} +type RefreshRes LoginRes diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..478f118 --- /dev/null +++ b/go.mod @@ -0,0 +1,36 @@ +module service.xpcool.com + +go 1.23.0 + +require github.com/gogf/gf/v2 v2.10.2 + +require ( + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grokify/html-strip-tags-go v0.1.0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/tablewriter v1.1.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + golang.org/x/crypto v0.38.0 + golang.org/x/net v0.40.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.25.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d74f4f0 --- /dev/null +++ b/go.sum @@ -0,0 +1,83 @@ +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU= +github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogf/gf/v2 v2.10.2 h1:46IO0Uc8e85/FqdftJFskfDejJLBL0JBnGS5qOftUu8= +github.com/gogf/gf/v2 v2.10.2/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= +github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= +github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY= +github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hack/config.yaml b/hack/config.yaml new file mode 100644 index 0000000..83c7dc6 --- /dev/null +++ b/hack/config.yaml @@ -0,0 +1,13 @@ + +# CLI tool, only in development environment. +# https://goframe.org/docs/cli +gfcli: + gen: + dao: + - link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test" + descriptionTag: true + + docker: + build: "-a amd64 -s linux -p temp -ew" + tagPrefixes: + - my.image.pub/my-app \ No newline at end of file diff --git a/hack/hack-cli.mk b/hack/hack-cli.mk new file mode 100644 index 0000000..526124e --- /dev/null +++ b/hack/hack-cli.mk @@ -0,0 +1,18 @@ + +# Install/Update to the latest CLI tool. +.PHONY: cli +cli: + @set -e; \ + echo "go install github.com/gogf/gf/cmd/gf/v2@latest"; \ + go install github.com/gogf/gf/cmd/gf/v2@latest; \ + echo "GoFame CLI installed successfully!" + + +# Check and install CLI tool. +.PHONY: cli.install +cli.install: + @set -e; \ + gf -v > /dev/null 2>&1 || if [[ "$?" -ne "0" ]]; then \ + echo "GoFame CLI is not installed, start proceeding auto installation..."; \ + make cli; \ + fi; \ No newline at end of file diff --git a/hack/hack.mk b/hack/hack.mk new file mode 100644 index 0000000..2f68179 --- /dev/null +++ b/hack/hack.mk @@ -0,0 +1,75 @@ +.DEFAULT_GOAL := build + +# Update GoFrame and its CLI to latest stable version. +.PHONY: up +up: cli.install + @gf up -a + +# Build binary using configuration from hack/config.yaml. +.PHONY: build +build: cli.install + @gf build -ew + +# Parse api and generate controller/sdk. +.PHONY: ctrl +ctrl: cli.install + @gf gen ctrl + +# Generate Go files for DAO/DO/Entity. +.PHONY: dao +dao: cli.install + @gf gen dao + +# Parse current project go files and generate enums go file. +.PHONY: enums +enums: cli.install + @gf gen enums + +# Generate Go files for Service. +.PHONY: service +service: cli.install + @gf gen service + + +# Build docker image. +.PHONY: image +image: cli.install + $(eval _TAG = $(shell git rev-parse --short HEAD)) +ifneq (, $(shell git status --porcelain 2>/dev/null)) + $(eval _TAG = $(_TAG).dirty) +endif + $(eval _TAG = $(if ${TAG}, ${TAG}, $(_TAG))) + $(eval _PUSH = $(if ${PUSH}, ${PUSH}, )) + @gf docker ${_PUSH} -tn $(DOCKER_NAME):${_TAG}; + + +# Build docker image and automatically push to docker repo. +.PHONY: image.push +image.push: cli.install + @make image PUSH=-p; + + +# Deploy image and yaml to current kubectl environment. +.PHONY: deploy +deploy: cli.install + $(eval _TAG = $(if ${TAG}, ${TAG}, develop)) + + @set -e; \ + mkdir -p $(ROOT_DIR)/temp/kustomize;\ + cd $(ROOT_DIR)/manifest/deploy/kustomize/overlays/${_ENV};\ + kustomize build > $(ROOT_DIR)/temp/kustomize.yaml;\ + kubectl apply -f $(ROOT_DIR)/temp/kustomize.yaml; \ + if [ $(DEPLOY_NAME) != "" ]; then \ + kubectl patch -n $(NAMESPACE) deployment/$(DEPLOY_NAME) -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"$(shell date +%s)\"}}}}}"; \ + fi; + + +# Parsing protobuf files and generating go files. +.PHONY: pb +pb: cli.install + @gf gen pb + +# Generate protobuf files for database tables. +.PHONY: pbentity +pbentity: cli.install + @gf gen pbentity \ No newline at end of file diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go new file mode 100644 index 0000000..18ded07 --- /dev/null +++ b/internal/cmd/cmd.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "context" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gcmd" + + adminctl "service.xpcool.com/internal/controller/admin" + "service.xpcool.com/internal/controller/hello" + userctl "service.xpcool.com/internal/controller/user" + "service.xpcool.com/internal/library/jwt" + "service.xpcool.com/internal/middleware" + "service.xpcool.com/internal/service" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "start http server", + Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { + s := g.Server() + tokens := jwt.New(ctx) + service.RegisterUserAuth(service.NewUserAuth(tokens, nil, nil)) + service.RegisterAdminAuth(service.NewAdminAuth(tokens)) + service.RegisterAdminAudit(service.NewAdminAudit()) + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(middleware.Recover, middleware.CORS) + group.Middleware(ghttp.MiddlewareHandlerResponse) + group.Bind( + hello.NewV1(), + ) + }) + s.Group("/api/v1", func(group *ghttp.RouterGroup) { + group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) + group.Bind(userctl.New()) // Login and refresh routes are public. + group.Group("/", func(protected *ghttp.RouterGroup) { protected.Middleware(middleware.UserAuth(tokens)) }) + }) + s.Group("/admin/v1", func(group *ghttp.RouterGroup) { + group.Middleware(middleware.Recover, middleware.CORS, ghttp.MiddlewareHandlerResponse) + group.Bind(adminctl.New()) // Admin login remains public; protected controllers mount separately. + group.Group("/", func(protected *ghttp.RouterGroup) { + protected.Middleware(middleware.AdminAuth(tokens, service.AdminAuth().HasPermission, func(ctx context.Context, id uint64, permission, method, path, ip, param string, duration, status int) { + service.AdminAudit().Record(ctx, service.AuditEvent{AdminID: id, Permission: permission, Method: method, Path: path, IP: ip, Param: param, DurationMS: duration, StatusCode: status}) + })) + }) + }) + s.Run() + return nil + }, + } +) diff --git a/internal/consts/consts.go b/internal/consts/consts.go new file mode 100644 index 0000000..d709a2b --- /dev/null +++ b/internal/consts/consts.go @@ -0,0 +1 @@ +package consts diff --git a/internal/consts/error_code.go b/internal/consts/error_code.go new file mode 100644 index 0000000..7a6b845 --- /dev/null +++ b/internal/consts/error_code.go @@ -0,0 +1,15 @@ +package consts + +const ( + CodeOK = 0 + CodeInvalidParam = 10001 + CodeUnauthorized = 10002 + CodeForbidden = 10003 + CodeInternal = 10004 + CodeUserNotFound = 20001 + CodeUserPasswordWrong = 20002 + CodeUserLoginType = 20003 + CodeAdminNotFound = 30001 + CodeAdminPasswordWrong = 30002 + CodeAdminPermissionDenied = 30003 +) diff --git a/internal/controller/admin/auth.go b/internal/controller/admin/auth.go new file mode 100644 index 0000000..eb94516 --- /dev/null +++ b/internal/controller/admin/auth.go @@ -0,0 +1,19 @@ +package admin + +import ( + "context" + adminv1 "service.xpcool.com/api/admin/v1" + "service.xpcool.com/internal/model/dto" + "service.xpcool.com/internal/service" +) + +type Controller struct{} + +func New() *Controller { return &Controller{} } +func (c *Controller) Login(ctx context.Context, req *adminv1.LoginReq) (res *adminv1.LoginRes, err error) { + p, id, err := service.AdminAuth().Login(ctx, dto.AdminLoginInput{Username: req.Username, Password: req.Password}) + if err != nil { + return nil, err + } + return &adminv1.LoginRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, AdminID: id}, nil +} diff --git a/internal/controller/hello/hello.go b/internal/controller/hello/hello.go new file mode 100644 index 0000000..f72082f --- /dev/null +++ b/internal/controller/hello/hello.go @@ -0,0 +1,5 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package hello diff --git a/internal/controller/hello/hello_new.go b/internal/controller/hello/hello_new.go new file mode 100644 index 0000000..b512e14 --- /dev/null +++ b/internal/controller/hello/hello_new.go @@ -0,0 +1,15 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package hello + +import ( + "service.xpcool.com/api/hello" +) + +type ControllerV1 struct{} + +func NewV1() hello.IHelloV1 { + return &ControllerV1{} +} diff --git a/internal/controller/hello/hello_v1_hello.go b/internal/controller/hello/hello_v1_hello.go new file mode 100644 index 0000000..d8e045f --- /dev/null +++ b/internal/controller/hello/hello_v1_hello.go @@ -0,0 +1,13 @@ +package hello + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + + "service.xpcool.com/api/hello/v1" +) + +func (c *ControllerV1) Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) { + g.RequestFromCtx(ctx).Response.Writeln("Hello World!") + return +} diff --git a/internal/controller/user/auth.go b/internal/controller/user/auth.go new file mode 100644 index 0000000..1ee92d4 --- /dev/null +++ b/internal/controller/user/auth.go @@ -0,0 +1,26 @@ +package user + +import ( + "context" + userv1 "service.xpcool.com/api/user/v1" + "service.xpcool.com/internal/model/dto" + "service.xpcool.com/internal/service" +) + +type Controller struct{} + +func New() *Controller { return &Controller{} } +func (c *Controller) Login(ctx context.Context, req *userv1.LoginReq) (res *userv1.LoginRes, err error) { + p, id, err := service.UserAuth().Login(ctx, dto.UserLoginInput{LoginType: req.LoginType, Code: req.Code, Mobile: req.Mobile, VerifyCode: req.VerifyCode, Account: req.Account, Password: req.Password, Terminal: req.Terminal}) + if err != nil { + return nil, err + } + return &userv1.LoginRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, UserID: id}, nil +} +func (c *Controller) Refresh(ctx context.Context, req *userv1.RefreshReq) (res *userv1.RefreshRes, err error) { + p, id, err := service.UserAuth().Refresh(ctx, req.RefreshToken) + if err != nil { + return nil, err + } + return &userv1.RefreshRes{AccessToken: p.AccessToken, RefreshToken: p.RefreshToken, ExpiresIn: p.ExpiresIn, UserID: id}, nil +} diff --git a/internal/dao/.gitkeep b/internal/dao/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/dao/admin_menu.go b/internal/dao/admin_menu.go new file mode 100644 index 0000000..425ba5c --- /dev/null +++ b/internal/dao/admin_menu.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminMenuDao is the data access object for the table admin_menu. +// You can define custom methods on it to extend its functionality as needed. +type adminMenuDao struct { + *internal.AdminMenuDao +} + +var ( + // AdminMenu is a globally accessible object for table admin_menu operations. + AdminMenu = adminMenuDao{internal.NewAdminMenuDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/admin_operation_log.go b/internal/dao/admin_operation_log.go new file mode 100644 index 0000000..060adad --- /dev/null +++ b/internal/dao/admin_operation_log.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminOperationLogDao is the data access object for the table admin_operation_log. +// You can define custom methods on it to extend its functionality as needed. +type adminOperationLogDao struct { + *internal.AdminOperationLogDao +} + +var ( + // AdminOperationLog is a globally accessible object for table admin_operation_log operations. + AdminOperationLog = adminOperationLogDao{internal.NewAdminOperationLogDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/admin_role.go b/internal/dao/admin_role.go new file mode 100644 index 0000000..440b6ff --- /dev/null +++ b/internal/dao/admin_role.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminRoleDao is the data access object for the table admin_role. +// You can define custom methods on it to extend its functionality as needed. +type adminRoleDao struct { + *internal.AdminRoleDao +} + +var ( + // AdminRole is a globally accessible object for table admin_role operations. + AdminRole = adminRoleDao{internal.NewAdminRoleDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/admin_role_menu.go b/internal/dao/admin_role_menu.go new file mode 100644 index 0000000..0474296 --- /dev/null +++ b/internal/dao/admin_role_menu.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminRoleMenuDao is the data access object for the table admin_role_menu. +// You can define custom methods on it to extend its functionality as needed. +type adminRoleMenuDao struct { + *internal.AdminRoleMenuDao +} + +var ( + // AdminRoleMenu is a globally accessible object for table admin_role_menu operations. + AdminRoleMenu = adminRoleMenuDao{internal.NewAdminRoleMenuDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/admin_user.go b/internal/dao/admin_user.go new file mode 100644 index 0000000..7b12f23 --- /dev/null +++ b/internal/dao/admin_user.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminUserDao is the data access object for the table admin_user. +// You can define custom methods on it to extend its functionality as needed. +type adminUserDao struct { + *internal.AdminUserDao +} + +var ( + // AdminUser is a globally accessible object for table admin_user operations. + AdminUser = adminUserDao{internal.NewAdminUserDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/admin_user_role.go b/internal/dao/admin_user_role.go new file mode 100644 index 0000000..16c37f9 --- /dev/null +++ b/internal/dao/admin_user_role.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// adminUserRoleDao is the data access object for the table admin_user_role. +// You can define custom methods on it to extend its functionality as needed. +type adminUserRoleDao struct { + *internal.AdminUserRoleDao +} + +var ( + // AdminUserRole is a globally accessible object for table admin_user_role operations. + AdminUserRole = adminUserRoleDao{internal.NewAdminUserRoleDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/auth_refresh_session.go b/internal/dao/auth_refresh_session.go new file mode 100644 index 0000000..b895353 --- /dev/null +++ b/internal/dao/auth_refresh_session.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// authRefreshSessionDao is the data access object for the table auth_refresh_session. +// You can define custom methods on it to extend its functionality as needed. +type authRefreshSessionDao struct { + *internal.AuthRefreshSessionDao +} + +var ( + // AuthRefreshSession is a globally accessible object for table auth_refresh_session operations. + AuthRefreshSession = authRefreshSessionDao{internal.NewAuthRefreshSessionDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/content.go b/internal/dao/content.go new file mode 100644 index 0000000..83d004b --- /dev/null +++ b/internal/dao/content.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// contentDao is the data access object for the table content. +// You can define custom methods on it to extend its functionality as needed. +type contentDao struct { + *internal.ContentDao +} + +var ( + // Content is a globally accessible object for table content operations. + Content = contentDao{internal.NewContentDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/internal/admin_menu.go b/internal/dao/internal/admin_menu.go new file mode 100644 index 0000000..2c3b0e0 --- /dev/null +++ b/internal/dao/internal/admin_menu.go @@ -0,0 +1,99 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminMenuDao is the data access object for the table admin_menu. +type AdminMenuDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminMenuColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminMenuColumns defines and stores column names for the table admin_menu. +type AdminMenuColumns struct { + Id string // + ParentId string // + Name string // + Type string // 1 menu,2 api + Path string // + Permission string // + Sort string // + Status string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// adminMenuColumns holds the columns for the table admin_menu. +var adminMenuColumns = AdminMenuColumns{ + Id: "id", + ParentId: "parent_id", + Name: "name", + Type: "type", + Path: "path", + Permission: "permission", + Sort: "sort", + Status: "status", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAdminMenuDao creates and returns a new DAO object for table data access. +func NewAdminMenuDao(handlers ...gdb.ModelHandler) *AdminMenuDao { + return &AdminMenuDao{ + group: "default", + table: "admin_menu", + columns: adminMenuColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AdminMenuDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AdminMenuDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AdminMenuDao) Columns() AdminMenuColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AdminMenuDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AdminMenuDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AdminMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/admin_operation_log.go b/internal/dao/internal/admin_operation_log.go new file mode 100644 index 0000000..77681a1 --- /dev/null +++ b/internal/dao/internal/admin_operation_log.go @@ -0,0 +1,101 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminOperationLogDao is the data access object for the table admin_operation_log. +type AdminOperationLogDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminOperationLogColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminOperationLogColumns defines and stores column names for the table admin_operation_log. +type AdminOperationLogColumns struct { + Id string // + AdminUserId string // + Permission string // + Method string // + Path string // + Ip string // + RequestParam string // + DurationMs string // + StatusCode string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// adminOperationLogColumns holds the columns for the table admin_operation_log. +var adminOperationLogColumns = AdminOperationLogColumns{ + Id: "id", + AdminUserId: "admin_user_id", + Permission: "permission", + Method: "method", + Path: "path", + Ip: "ip", + RequestParam: "request_param", + DurationMs: "duration_ms", + StatusCode: "status_code", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAdminOperationLogDao creates and returns a new DAO object for table data access. +func NewAdminOperationLogDao(handlers ...gdb.ModelHandler) *AdminOperationLogDao { + return &AdminOperationLogDao{ + group: "default", + table: "admin_operation_log", + columns: adminOperationLogColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AdminOperationLogDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AdminOperationLogDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AdminOperationLogDao) Columns() AdminOperationLogColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AdminOperationLogDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AdminOperationLogDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AdminOperationLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/admin_role.go b/internal/dao/internal/admin_role.go new file mode 100644 index 0000000..9acb9b4 --- /dev/null +++ b/internal/dao/internal/admin_role.go @@ -0,0 +1,91 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminRoleDao is the data access object for the table admin_role. +type AdminRoleDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminRoleColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminRoleColumns defines and stores column names for the table admin_role. +type AdminRoleColumns struct { + Id string // + Code string // + Name string // + Status string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// adminRoleColumns holds the columns for the table admin_role. +var adminRoleColumns = AdminRoleColumns{ + Id: "id", + Code: "code", + Name: "name", + Status: "status", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAdminRoleDao creates and returns a new DAO object for table data access. +func NewAdminRoleDao(handlers ...gdb.ModelHandler) *AdminRoleDao { + return &AdminRoleDao{ + group: "default", + table: "admin_role", + columns: adminRoleColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AdminRoleDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AdminRoleDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AdminRoleDao) Columns() AdminRoleColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AdminRoleDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AdminRoleDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AdminRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/admin_role_menu.go b/internal/dao/internal/admin_role_menu.go new file mode 100644 index 0000000..0a2e2ea --- /dev/null +++ b/internal/dao/internal/admin_role_menu.go @@ -0,0 +1,89 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminRoleMenuDao is the data access object for the table admin_role_menu. +type AdminRoleMenuDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminRoleMenuColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminRoleMenuColumns defines and stores column names for the table admin_role_menu. +type AdminRoleMenuColumns struct { + Id string // + RoleId string // + MenuId string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// adminRoleMenuColumns holds the columns for the table admin_role_menu. +var adminRoleMenuColumns = AdminRoleMenuColumns{ + Id: "id", + RoleId: "role_id", + MenuId: "menu_id", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAdminRoleMenuDao creates and returns a new DAO object for table data access. +func NewAdminRoleMenuDao(handlers ...gdb.ModelHandler) *AdminRoleMenuDao { + return &AdminRoleMenuDao{ + group: "default", + table: "admin_role_menu", + columns: adminRoleMenuColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AdminRoleMenuDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AdminRoleMenuDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AdminRoleMenuDao) Columns() AdminRoleMenuColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AdminRoleMenuDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AdminRoleMenuDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AdminRoleMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/admin_user.go b/internal/dao/internal/admin_user.go new file mode 100644 index 0000000..fb8f0cc --- /dev/null +++ b/internal/dao/internal/admin_user.go @@ -0,0 +1,95 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminUserDao is the data access object for the table admin_user. +type AdminUserDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminUserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminUserColumns defines and stores column names for the table admin_user. +type AdminUserColumns struct { + Id string // + Username string // + PasswordHash string // + Nickname string // + Status string // + LastLoginAt string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// adminUserColumns holds the columns for the table admin_user. +var adminUserColumns = AdminUserColumns{ + Id: "id", + Username: "username", + PasswordHash: "password_hash", + Nickname: "nickname", + Status: "status", + LastLoginAt: "last_login_at", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAdminUserDao creates and returns a new DAO object for table data access. +func NewAdminUserDao(handlers ...gdb.ModelHandler) *AdminUserDao { + return &AdminUserDao{ + group: "default", + table: "admin_user", + columns: adminUserColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AdminUserDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AdminUserDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AdminUserDao) Columns() AdminUserColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AdminUserDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AdminUserDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AdminUserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/admin_user_role.go b/internal/dao/internal/admin_user_role.go new file mode 100644 index 0000000..d86f632 --- /dev/null +++ b/internal/dao/internal/admin_user_role.go @@ -0,0 +1,89 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AdminUserRoleDao is the data access object for the table admin_user_role. +type AdminUserRoleDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AdminUserRoleColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AdminUserRoleColumns defines and stores column names for the table admin_user_role. +type AdminUserRoleColumns struct { + Id string // + AdminUserId string // + RoleId string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// adminUserRoleColumns holds the columns for the table admin_user_role. +var adminUserRoleColumns = AdminUserRoleColumns{ + Id: "id", + AdminUserId: "admin_user_id", + RoleId: "role_id", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAdminUserRoleDao creates and returns a new DAO object for table data access. +func NewAdminUserRoleDao(handlers ...gdb.ModelHandler) *AdminUserRoleDao { + return &AdminUserRoleDao{ + group: "default", + table: "admin_user_role", + columns: adminUserRoleColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AdminUserRoleDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AdminUserRoleDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AdminUserRoleDao) Columns() AdminUserRoleColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AdminUserRoleDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AdminUserRoleDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AdminUserRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/auth_refresh_session.go b/internal/dao/internal/auth_refresh_session.go new file mode 100644 index 0000000..5e89a38 --- /dev/null +++ b/internal/dao/internal/auth_refresh_session.go @@ -0,0 +1,97 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// AuthRefreshSessionDao is the data access object for the table auth_refresh_session. +type AuthRefreshSessionDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns AuthRefreshSessionColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// AuthRefreshSessionColumns defines and stores column names for the table auth_refresh_session. +type AuthRefreshSessionColumns struct { + Id string // + SubjectId string // ????????? ID + Scope string // user/admin + Terminal string // mini/h5/app + Jti string // JWT ?????? + ExpiredAt string // + RevokedAt string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// authRefreshSessionColumns holds the columns for the table auth_refresh_session. +var authRefreshSessionColumns = AuthRefreshSessionColumns{ + Id: "id", + SubjectId: "subject_id", + Scope: "scope", + Terminal: "terminal", + Jti: "jti", + ExpiredAt: "expired_at", + RevokedAt: "revoked_at", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewAuthRefreshSessionDao creates and returns a new DAO object for table data access. +func NewAuthRefreshSessionDao(handlers ...gdb.ModelHandler) *AuthRefreshSessionDao { + return &AuthRefreshSessionDao{ + group: "default", + table: "auth_refresh_session", + columns: authRefreshSessionColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *AuthRefreshSessionDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *AuthRefreshSessionDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *AuthRefreshSessionDao) Columns() AuthRefreshSessionColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *AuthRefreshSessionDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *AuthRefreshSessionDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *AuthRefreshSessionDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/content.go b/internal/dao/internal/content.go new file mode 100644 index 0000000..9c803d9 --- /dev/null +++ b/internal/dao/internal/content.go @@ -0,0 +1,91 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// ContentDao is the data access object for the table content. +type ContentDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns ContentColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// ContentColumns defines and stores column names for the table content. +type ContentColumns struct { + Id string // + Title string // + Body string // + Status string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// contentColumns holds the columns for the table content. +var contentColumns = ContentColumns{ + Id: "id", + Title: "title", + Body: "body", + Status: "status", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewContentDao creates and returns a new DAO object for table data access. +func NewContentDao(handlers ...gdb.ModelHandler) *ContentDao { + return &ContentDao{ + group: "default", + table: "content", + columns: contentColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *ContentDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *ContentDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *ContentDao) Columns() ContentColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *ContentDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *ContentDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *ContentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/user.go b/internal/dao/internal/user.go new file mode 100644 index 0000000..f0a9e95 --- /dev/null +++ b/internal/dao/internal/user.go @@ -0,0 +1,103 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// UserDao is the data access object for the table user. +type UserDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns UserColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// UserColumns defines and stores column names for the table user. +type UserColumns struct { + Id string // + UnionId string // + OpenId string // + Mobile string // + Account string // + PasswordHash string // + Nickname string // + Avatar string // + Status string // + LastLoginAt string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// userColumns holds the columns for the table user. +var userColumns = UserColumns{ + Id: "id", + UnionId: "union_id", + OpenId: "open_id", + Mobile: "mobile", + Account: "account", + PasswordHash: "password_hash", + Nickname: "nickname", + Avatar: "avatar", + Status: "status", + LastLoginAt: "last_login_at", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewUserDao creates and returns a new DAO object for table data access. +func NewUserDao(handlers ...gdb.ModelHandler) *UserDao { + return &UserDao{ + group: "default", + table: "user", + columns: userColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *UserDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *UserDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *UserDao) Columns() UserColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *UserDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *UserDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *UserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/user_favorite.go b/internal/dao/internal/user_favorite.go new file mode 100644 index 0000000..9b52a32 --- /dev/null +++ b/internal/dao/internal/user_favorite.go @@ -0,0 +1,89 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// UserFavoriteDao is the data access object for the table user_favorite. +type UserFavoriteDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns UserFavoriteColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// UserFavoriteColumns defines and stores column names for the table user_favorite. +type UserFavoriteColumns struct { + Id string // + UserId string // + ContentId string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// userFavoriteColumns holds the columns for the table user_favorite. +var userFavoriteColumns = UserFavoriteColumns{ + Id: "id", + UserId: "user_id", + ContentId: "content_id", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewUserFavoriteDao creates and returns a new DAO object for table data access. +func NewUserFavoriteDao(handlers ...gdb.ModelHandler) *UserFavoriteDao { + return &UserFavoriteDao{ + group: "default", + table: "user_favorite", + columns: userFavoriteColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *UserFavoriteDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *UserFavoriteDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *UserFavoriteDao) Columns() UserFavoriteColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *UserFavoriteDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *UserFavoriteDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *UserFavoriteDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/user_message.go b/internal/dao/internal/user_message.go new file mode 100644 index 0000000..9389c1a --- /dev/null +++ b/internal/dao/internal/user_message.go @@ -0,0 +1,93 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// UserMessageDao is the data access object for the table user_message. +type UserMessageDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns UserMessageColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// UserMessageColumns defines and stores column names for the table user_message. +type UserMessageColumns struct { + Id string // + UserId string // + Title string // + Content string // + IsRead string // + CreatedAt string // + UpdatedAt string // + DeletedAt string // +} + +// userMessageColumns holds the columns for the table user_message. +var userMessageColumns = UserMessageColumns{ + Id: "id", + UserId: "user_id", + Title: "title", + Content: "content", + IsRead: "is_read", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewUserMessageDao creates and returns a new DAO object for table data access. +func NewUserMessageDao(handlers ...gdb.ModelHandler) *UserMessageDao { + return &UserMessageDao{ + group: "default", + table: "user_message", + columns: userMessageColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *UserMessageDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *UserMessageDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *UserMessageDao) Columns() UserMessageColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *UserMessageDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *UserMessageDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *UserMessageDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/user.go b/internal/dao/user.go new file mode 100644 index 0000000..acb129f --- /dev/null +++ b/internal/dao/user.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// userDao is the data access object for the table user. +// You can define custom methods on it to extend its functionality as needed. +type userDao struct { + *internal.UserDao +} + +var ( + // User is a globally accessible object for table user operations. + User = userDao{internal.NewUserDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/user_favorite.go b/internal/dao/user_favorite.go new file mode 100644 index 0000000..b595e82 --- /dev/null +++ b/internal/dao/user_favorite.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// userFavoriteDao is the data access object for the table user_favorite. +// You can define custom methods on it to extend its functionality as needed. +type userFavoriteDao struct { + *internal.UserFavoriteDao +} + +var ( + // UserFavorite is a globally accessible object for table user_favorite operations. + UserFavorite = userFavoriteDao{internal.NewUserFavoriteDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/dao/user_message.go b/internal/dao/user_message.go new file mode 100644 index 0000000..00c348d --- /dev/null +++ b/internal/dao/user_message.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "service.xpcool.com/internal/dao/internal" +) + +// userMessageDao is the data access object for the table user_message. +// You can define custom methods on it to extend its functionality as needed. +type userMessageDao struct { + *internal.UserMessageDao +} + +var ( + // UserMessage is a globally accessible object for table user_message operations. + UserMessage = userMessageDao{internal.NewUserMessageDao()} +) + +// Add your custom methods and functionality below. diff --git a/internal/library/jwt/jwt.go b/internal/library/jwt/jwt.go new file mode 100644 index 0000000..8ec42b8 --- /dev/null +++ b/internal/library/jwt/jwt.go @@ -0,0 +1,92 @@ +// Package jwt implements HS256 JWT with Go standard crypto primitives. +package jwt + +import ( + "context" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "strings" + "time" + + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" +) + +type Claims struct { + Subject uint64 `json:"sub"` + Scope string `json:"scope"` + Terminal string `json:"terminal,omitempty"` + Type string `json:"typ"` + ExpireAt int64 `json:"exp"` + IssuedAt int64 `json:"iat"` + JTI string `json:"jti,omitempty"` // 刷新令牌的唯一编号,用于数据库会话校验。 +} +type Service struct { + secret []byte + accessTTL, refreshTTL time.Duration +} + +func New(ctx context.Context) *Service { + // 密钥和有效期由配置中心统一提供,生产环境必须通过环境变量覆盖。 + return &Service{secret: []byte(g.Cfg().MustGet(ctx, "jwt.secret").String()), accessTTL: g.Cfg().MustGet(ctx, "jwt.accessExpire").Duration(), refreshTTL: g.Cfg().MustGet(ctx, "jwt.refreshExpire").Duration()} +} +func (s *Service) Issue(id uint64, scope, terminal string) (access, refresh string, expires int64, err error) { + // access token 只负责短期访问;refresh token 带唯一 JTI,用于可撤销的长期会话。 + now := time.Now().Unix() + expires = now + int64(s.accessTTL.Seconds()) + access, err = s.sign(Claims{Subject: id, Scope: scope, Terminal: terminal, Type: "access", ExpireAt: expires, IssuedAt: now}) + if err != nil { + return + } + jti, err := newJTI() + if err != nil { + return "", "", 0, gerror.Wrap(err, "生成刷新令牌标识失败") + } + refresh, err = s.sign(Claims{Subject: id, Scope: scope, Terminal: terminal, Type: "refresh", ExpireAt: now + int64(s.refreshTTL.Seconds()), IssuedAt: now, JTI: jti}) + return +} +func (s *Service) Parse(token, typ, scope string) (*Claims, error) { + // 先使用常量时间比较校验签名,再解析声明,避免伪造令牌进入业务层。 + parts := strings.Split(token, ".") + if len(parts) != 3 || !hmac.Equal([]byte(parts[2]), []byte(s.signature(parts[0]+"."+parts[1]))) { + return nil, gerror.New("invalid token") + } + raw, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, gerror.Wrap(err, "decode token") + } + var c Claims + if err = json.Unmarshal(raw, &c); err != nil { + return nil, gerror.Wrap(err, "decode claims") + } + if c.ExpireAt < time.Now().Unix() || c.Type != typ || c.Scope != scope { + return nil, gerror.New("token expired or scope mismatch") + } + return &c, nil +} + +// newJTI 生成 256 位随机标识,编码后正好适配数据库 CHAR(43) 字段。 +func newJTI() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(b), nil +} +func (s *Service) sign(c Claims) (string, error) { + h := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`)) + b, err := json.Marshal(c) + if err != nil { + return "", gerror.Wrap(err, "encode claims") + } + p := h + "." + base64.RawURLEncoding.EncodeToString(b) + return p + "." + s.signature(p), nil +} +func (s *Service) signature(input string) string { + mac := hmac.New(sha256.New, s.secret) + _, _ = mac.Write([]byte(input)) + return base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) +} diff --git a/internal/library/page/page.go b/internal/library/page/page.go new file mode 100644 index 0000000..6389a34 --- /dev/null +++ b/internal/library/page/page.go @@ -0,0 +1,13 @@ +package page + +const ( + DefaultSize = 20 + MaxSize = 100 +) + +type Input struct { + Page int `json:"page" v:"min:1#page must be >= 1"` + PageSize int `json:"pageSize" v:"min:1|max:100#pageSize must be 1-100"` +} + +func (in Input) Offset() int { return (in.Page - 1) * in.PageSize } diff --git a/internal/library/response/error.go b/internal/library/response/error.go new file mode 100644 index 0000000..9af0141 --- /dev/null +++ b/internal/library/response/error.go @@ -0,0 +1,11 @@ +package response + +import ( + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" +) + +// Error creates an error carrying a stable application code for global response handling. +func Error(code int, message string) error { + return gerror.NewCode(gcode.New(code, message, nil), message) +} diff --git a/internal/library/response/response.go b/internal/library/response/response.go new file mode 100644 index 0000000..8394214 --- /dev/null +++ b/internal/library/response/response.go @@ -0,0 +1,15 @@ +package response + +import "github.com/gogf/gf/v2/net/ghttp" + +// Body is the only JSON envelope exposed by both API surfaces. +type Body struct { + Code int `json:"code"` + Message string `json:"message"` + Data any `json:"data,omitempty"` +} + +func JSON(r *ghttp.Request, code int, message string, data any) { + r.Response.WriteJsonExit(Body{Code: code, Message: message, Data: data}) +} +func OK(r *ghttp.Request, data any) { JSON(r, 0, "ok", data) } diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go new file mode 100644 index 0000000..ca0f25f --- /dev/null +++ b/internal/middleware/auth.go @@ -0,0 +1,71 @@ +package middleware + +import ( + "context" + "github.com/gogf/gf/v2/net/ghttp" + "service.xpcool.com/internal/consts" + "service.xpcool.com/internal/library/jwt" + "service.xpcool.com/internal/library/response" + "strings" + "time" +) + +type contextKey string + +const ( + UserIDKey contextKey = "userId" + AdminIDKey contextKey = "adminId" + PermissionKey contextKey = "permission" +) + +func bearer(r *ghttp.Request) string { + // 统一从 Authorization: Bearer 提取令牌。 + return strings.TrimSpace(strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer")) +} +func auditParam(raw string) string { + // 操作日志需要参数用于审计,但绝不记录密码、令牌和验证码等敏感字段。 + lower := strings.ToLower(raw) + if strings.Contains(lower, "password") || strings.Contains(lower, "token") || strings.Contains(lower, "code") { + return "[redacted]" + } + return raw +} +func UserAuth(s *jwt.Service) ghttp.HandlerFunc { + // 用户端仅接受 scope=user 的 access token。 + return func(r *ghttp.Request) { + c, err := s.Parse(bearer(r), "access", "user") + if err != nil { + response.JSON(r, consts.CodeUnauthorized, "login required", nil) + return + } + r.SetCtxVar(UserIDKey, c.Subject) + r.Middleware.Next() + } +} +func AdminAuth(s *jwt.Service, permissionCheck func(context.Context, uint64, string) (bool, error), audit func(context.Context, uint64, string, string, string, string, string, int, int)) ghttp.HandlerFunc { + // 管理端在令牌通过后继续校验 X-Permission 对应的 RBAC 权限,并在请求结束后记审计日志。 + return func(r *ghttp.Request) { + start := time.Now() + c, err := s.Parse(bearer(r), "access", "admin") + if err != nil { + response.JSON(r, consts.CodeUnauthorized, "admin login required", nil) + return + } + permission := r.Header.Get("X-Permission") + if permission == "" { + response.JSON(r, consts.CodeForbidden, "permission identifier required", nil) + return + } + ok, err := permissionCheck(r.Context(), c.Subject, permission) + if err != nil || !ok { + response.JSON(r, consts.CodeAdminPermissionDenied, "permission denied", nil) + return + } + defer func() { + audit(r.Context(), c.Subject, permission, r.Method, r.URL.Path, r.GetClientIp(), auditParam(r.GetBodyString()), int(time.Since(start).Milliseconds()), r.Response.Status) + }() + r.SetCtxVar(AdminIDKey, c.Subject) + r.SetCtxVar(PermissionKey, permission) + r.Middleware.Next() + } +} diff --git a/internal/middleware/common.go b/internal/middleware/common.go new file mode 100644 index 0000000..2e71c74 --- /dev/null +++ b/internal/middleware/common.go @@ -0,0 +1,26 @@ +package middleware + +import ( + "github.com/gogf/gf/v2/net/ghttp" + "service.xpcool.com/internal/consts" + "service.xpcool.com/internal/library/response" +) + +// CORS is deliberately mounted only on public HTTP route groups. +func CORS(r *ghttp.Request) { + r.Response.CORSDefault() + if r.Method == "OPTIONS" { + r.Response.WriteStatusExit(204) + } + r.Middleware.Next() +} + +// Recover converts panics to the common envelope; server logs retain stack traces. +func Recover(r *ghttp.Request) { + defer func() { + if recover() != nil { + response.JSON(r, consts.CodeInternal, "internal server error", nil) + } + }() + r.Middleware.Next() +} diff --git a/internal/model/.gitkeep b/internal/model/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/model/do/.gitkeep b/internal/model/do/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/model/do/admin_menu.go b/internal/model/do/admin_menu.go new file mode 100644 index 0000000..20d066c --- /dev/null +++ b/internal/model/do/admin_menu.go @@ -0,0 +1,26 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminMenu is the golang structure of table admin_menu for DAO operations like Where/Data. +type AdminMenu struct { + g.Meta `orm:"table:admin_menu, do:true"` + Id any // + ParentId any // + Name any // + Type any // 1 menu,2 api + Path any // + Permission any // + Sort any // + Status any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/admin_operation_log.go b/internal/model/do/admin_operation_log.go new file mode 100644 index 0000000..1d45785 --- /dev/null +++ b/internal/model/do/admin_operation_log.go @@ -0,0 +1,27 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminOperationLog is the golang structure of table admin_operation_log for DAO operations like Where/Data. +type AdminOperationLog struct { + g.Meta `orm:"table:admin_operation_log, do:true"` + Id any // + AdminUserId any // + Permission any // + Method any // + Path any // + Ip any // + RequestParam any // + DurationMs any // + StatusCode any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/admin_role.go b/internal/model/do/admin_role.go new file mode 100644 index 0000000..849fe41 --- /dev/null +++ b/internal/model/do/admin_role.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminRole is the golang structure of table admin_role for DAO operations like Where/Data. +type AdminRole struct { + g.Meta `orm:"table:admin_role, do:true"` + Id any // + Code any // + Name any // + Status any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/admin_role_menu.go b/internal/model/do/admin_role_menu.go new file mode 100644 index 0000000..f78004e --- /dev/null +++ b/internal/model/do/admin_role_menu.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminRoleMenu is the golang structure of table admin_role_menu for DAO operations like Where/Data. +type AdminRoleMenu struct { + g.Meta `orm:"table:admin_role_menu, do:true"` + Id any // + RoleId any // + MenuId any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/admin_user.go b/internal/model/do/admin_user.go new file mode 100644 index 0000000..5072be2 --- /dev/null +++ b/internal/model/do/admin_user.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminUser is the golang structure of table admin_user for DAO operations like Where/Data. +type AdminUser struct { + g.Meta `orm:"table:admin_user, do:true"` + Id any // + Username any // + PasswordHash any // + Nickname any // + Status any // + LastLoginAt *gtime.Time // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/admin_user_role.go b/internal/model/do/admin_user_role.go new file mode 100644 index 0000000..2fbd1a2 --- /dev/null +++ b/internal/model/do/admin_user_role.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminUserRole is the golang structure of table admin_user_role for DAO operations like Where/Data. +type AdminUserRole struct { + g.Meta `orm:"table:admin_user_role, do:true"` + Id any // + AdminUserId any // + RoleId any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/auth_refresh_session.go b/internal/model/do/auth_refresh_session.go new file mode 100644 index 0000000..c68929a --- /dev/null +++ b/internal/model/do/auth_refresh_session.go @@ -0,0 +1,25 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// AuthRefreshSession is the golang structure of table auth_refresh_session for DAO operations like Where/Data. +type AuthRefreshSession struct { + g.Meta `orm:"table:auth_refresh_session, do:true"` + Id any // + SubjectId any // ????????? ID + Scope any // user/admin + Terminal any // mini/h5/app + Jti any // JWT ?????? + ExpiredAt *gtime.Time // + RevokedAt *gtime.Time // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/content.go b/internal/model/do/content.go new file mode 100644 index 0000000..a5ff59f --- /dev/null +++ b/internal/model/do/content.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Content is the golang structure of table content for DAO operations like Where/Data. +type Content struct { + g.Meta `orm:"table:content, do:true"` + Id any // + Title any // + Body any // + Status any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/user.go b/internal/model/do/user.go new file mode 100644 index 0000000..aa8cfd4 --- /dev/null +++ b/internal/model/do/user.go @@ -0,0 +1,28 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// User is the golang structure of table user for DAO operations like Where/Data. +type User struct { + g.Meta `orm:"table:user, do:true"` + Id any // + UnionId any // + OpenId any // + Mobile any // + Account any // + PasswordHash any // + Nickname any // + Avatar any // + Status any // + LastLoginAt *gtime.Time // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/user_favorite.go b/internal/model/do/user_favorite.go new file mode 100644 index 0000000..6c2398a --- /dev/null +++ b/internal/model/do/user_favorite.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// UserFavorite is the golang structure of table user_favorite for DAO operations like Where/Data. +type UserFavorite struct { + g.Meta `orm:"table:user_favorite, do:true"` + Id any // + UserId any // + ContentId any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/do/user_message.go b/internal/model/do/user_message.go new file mode 100644 index 0000000..26e4e28 --- /dev/null +++ b/internal/model/do/user_message.go @@ -0,0 +1,23 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// UserMessage is the golang structure of table user_message for DAO operations like Where/Data. +type UserMessage struct { + g.Meta `orm:"table:user_message, do:true"` + Id any // + UserId any // + Title any // + Content any // + IsRead any // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + DeletedAt *gtime.Time // +} diff --git a/internal/model/dto/auth.go b/internal/model/dto/auth.go new file mode 100644 index 0000000..fe4fbae --- /dev/null +++ b/internal/model/dto/auth.go @@ -0,0 +1,9 @@ +package dto + +type UserLoginInput struct{ LoginType, Code, Mobile, VerifyCode, Account, Password, Terminal string } +type AdminLoginInput struct{ Username, Password string } +type TokenPair struct { + AccessToken string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int64 `json:"expiresIn"` +} diff --git a/internal/model/entity/.gitkeep b/internal/model/entity/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/model/entity/admin_menu.go b/internal/model/entity/admin_menu.go new file mode 100644 index 0000000..10ccf16 --- /dev/null +++ b/internal/model/entity/admin_menu.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminMenu is the golang structure for table admin_menu. +type AdminMenu struct { + Id uint64 `json:"id" orm:"id" description:""` // + ParentId uint64 `json:"parentId" orm:"parent_id" description:""` // + Name string `json:"name" orm:"name" description:""` // + Type int `json:"type" orm:"type" description:"1 menu,2 api"` // 1 menu,2 api + Path string `json:"path" orm:"path" description:""` // + Permission string `json:"permission" orm:"permission" description:""` // + Sort int `json:"sort" orm:"sort" description:""` // + Status int `json:"status" orm:"status" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/admin_operation_log.go b/internal/model/entity/admin_operation_log.go new file mode 100644 index 0000000..48c9036 --- /dev/null +++ b/internal/model/entity/admin_operation_log.go @@ -0,0 +1,25 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminOperationLog is the golang structure for table admin_operation_log. +type AdminOperationLog struct { + Id uint64 `json:"id" orm:"id" description:""` // + AdminUserId uint64 `json:"adminUserId" orm:"admin_user_id" description:""` // + Permission string `json:"permission" orm:"permission" description:""` // + Method string `json:"method" orm:"method" description:""` // + Path string `json:"path" orm:"path" description:""` // + Ip string `json:"ip" orm:"ip" description:""` // + RequestParam string `json:"requestParam" orm:"request_param" description:""` // + DurationMs uint `json:"durationMs" orm:"duration_ms" description:""` // + StatusCode int `json:"statusCode" orm:"status_code" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/admin_role.go b/internal/model/entity/admin_role.go new file mode 100644 index 0000000..eda5d0c --- /dev/null +++ b/internal/model/entity/admin_role.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminRole is the golang structure for table admin_role. +type AdminRole struct { + Id uint64 `json:"id" orm:"id" description:""` // + Code string `json:"code" orm:"code" description:""` // + Name string `json:"name" orm:"name" description:""` // + Status int `json:"status" orm:"status" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/admin_role_menu.go b/internal/model/entity/admin_role_menu.go new file mode 100644 index 0000000..4e0796d --- /dev/null +++ b/internal/model/entity/admin_role_menu.go @@ -0,0 +1,19 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminRoleMenu is the golang structure for table admin_role_menu. +type AdminRoleMenu struct { + Id uint64 `json:"id" orm:"id" description:""` // + RoleId uint64 `json:"roleId" orm:"role_id" description:""` // + MenuId uint64 `json:"menuId" orm:"menu_id" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/admin_user.go b/internal/model/entity/admin_user.go new file mode 100644 index 0000000..2823d66 --- /dev/null +++ b/internal/model/entity/admin_user.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminUser is the golang structure for table admin_user. +type AdminUser struct { + Id uint64 `json:"id" orm:"id" description:""` // + Username string `json:"username" orm:"username" description:""` // + PasswordHash string `json:"passwordHash" orm:"password_hash" description:""` // + Nickname string `json:"nickname" orm:"nickname" description:""` // + Status int `json:"status" orm:"status" description:""` // + LastLoginAt *gtime.Time `json:"lastLoginAt" orm:"last_login_at" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/admin_user_role.go b/internal/model/entity/admin_user_role.go new file mode 100644 index 0000000..002cdf0 --- /dev/null +++ b/internal/model/entity/admin_user_role.go @@ -0,0 +1,19 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AdminUserRole is the golang structure for table admin_user_role. +type AdminUserRole struct { + Id uint64 `json:"id" orm:"id" description:""` // + AdminUserId uint64 `json:"adminUserId" orm:"admin_user_id" description:""` // + RoleId uint64 `json:"roleId" orm:"role_id" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/auth_refresh_session.go b/internal/model/entity/auth_refresh_session.go new file mode 100644 index 0000000..3c3c686 --- /dev/null +++ b/internal/model/entity/auth_refresh_session.go @@ -0,0 +1,23 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// AuthRefreshSession is the golang structure for table auth_refresh_session. +type AuthRefreshSession struct { + Id uint64 `json:"id" orm:"id" description:""` // + SubjectId uint64 `json:"subjectId" orm:"subject_id" description:"????????? ID"` // ????????? ID + Scope string `json:"scope" orm:"scope" description:"user/admin"` // user/admin + Terminal string `json:"terminal" orm:"terminal" description:"mini/h5/app"` // mini/h5/app + Jti string `json:"jti" orm:"jti" description:"JWT ??????"` // JWT ?????? + ExpiredAt *gtime.Time `json:"expiredAt" orm:"expired_at" description:""` // + RevokedAt *gtime.Time `json:"revokedAt" orm:"revoked_at" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/content.go b/internal/model/entity/content.go new file mode 100644 index 0000000..5491afd --- /dev/null +++ b/internal/model/entity/content.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Content is the golang structure for table content. +type Content struct { + Id uint64 `json:"id" orm:"id" description:""` // + Title string `json:"title" orm:"title" description:""` // + Body string `json:"body" orm:"body" description:""` // + Status int `json:"status" orm:"status" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/user.go b/internal/model/entity/user.go new file mode 100644 index 0000000..f66dc8e --- /dev/null +++ b/internal/model/entity/user.go @@ -0,0 +1,26 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// User is the golang structure for table user. +type User struct { + Id uint64 `json:"id" orm:"id" description:""` // + UnionId string `json:"unionId" orm:"union_id" description:""` // + OpenId string `json:"openId" orm:"open_id" description:""` // + Mobile string `json:"mobile" orm:"mobile" description:""` // + Account string `json:"account" orm:"account" description:""` // + PasswordHash string `json:"passwordHash" orm:"password_hash" description:""` // + Nickname string `json:"nickname" orm:"nickname" description:""` // + Avatar string `json:"avatar" orm:"avatar" description:""` // + Status int `json:"status" orm:"status" description:""` // + LastLoginAt *gtime.Time `json:"lastLoginAt" orm:"last_login_at" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/user_favorite.go b/internal/model/entity/user_favorite.go new file mode 100644 index 0000000..ba4cd7b --- /dev/null +++ b/internal/model/entity/user_favorite.go @@ -0,0 +1,19 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// UserFavorite is the golang structure for table user_favorite. +type UserFavorite struct { + Id uint64 `json:"id" orm:"id" description:""` // + UserId uint64 `json:"userId" orm:"user_id" description:""` // + ContentId uint64 `json:"contentId" orm:"content_id" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/entity/user_message.go b/internal/model/entity/user_message.go new file mode 100644 index 0000000..0f314ce --- /dev/null +++ b/internal/model/entity/user_message.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// UserMessage is the golang structure for table user_message. +type UserMessage struct { + Id uint64 `json:"id" orm:"id" description:""` // + UserId uint64 `json:"userId" orm:"user_id" description:""` // + Title string `json:"title" orm:"title" description:""` // + Content string `json:"content" orm:"content" description:""` // + IsRead int `json:"isRead" orm:"is_read" description:""` // + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:""` // + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:""` // + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:""` // +} diff --git a/internal/model/vo/auth.go b/internal/model/vo/auth.go new file mode 100644 index 0000000..d947537 --- /dev/null +++ b/internal/model/vo/auth.go @@ -0,0 +1,9 @@ +package vo + +import "service.xpcool.com/internal/model/dto" + +type Login struct { + Token dto.TokenPair `json:"token"` + UserID uint64 `json:"userId"` + Terminal string `json:"terminal,omitempty"` +} diff --git a/internal/service/.gitkeep b/internal/service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/service/admin_audit.go b/internal/service/admin_audit.go new file mode 100644 index 0000000..2fd7866 --- /dev/null +++ b/internal/service/admin_audit.go @@ -0,0 +1,20 @@ +package service + +import ( + "context" + "github.com/gogf/gf/v2/os/glog" + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/model/do" +) + +type adminAudit struct{} + +func NewAdminAudit() IAdminAudit { return &adminAudit{} } + +// Record 采用尽力而为策略:审计落库失败会记录系统日志,但不影响原业务请求结果。 +func (a *adminAudit) Record(ctx context.Context, e AuditEvent) { + _, err := dao.AdminOperationLog.Ctx(ctx).Data(do.AdminOperationLog{AdminUserId: e.AdminID, Permission: e.Permission, Method: e.Method, Path: e.Path, Ip: e.IP, RequestParam: e.Param, DurationMs: e.DurationMS, StatusCode: e.StatusCode}).Insert() + if err != nil { + glog.Error(ctx, err) + } +} diff --git a/internal/service/admin_auth.go b/internal/service/admin_auth.go new file mode 100644 index 0000000..a602ccb --- /dev/null +++ b/internal/service/admin_auth.go @@ -0,0 +1,44 @@ +package service + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "golang.org/x/crypto/bcrypt" + "service.xpcool.com/internal/consts" + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/library/jwt" + "service.xpcool.com/internal/library/response" + "service.xpcool.com/internal/model/do" + "service.xpcool.com/internal/model/dto" + "service.xpcool.com/internal/model/entity" +) + +type adminAuth struct{ tokens *jwt.Service } + +func NewAdminAuth(tokens *jwt.Service) IAdminAuth { return &adminAuth{tokens} } +func (s *adminAuth) Login(ctx context.Context, in dto.AdminLoginInput) (*dto.TokenPair, uint64, error) { + // 管理端只允许账号密码登录,状态异常或密码错误均返回统一错误,避免枚举账号。 + var a entity.AdminUser + if err := dao.AdminUser.Ctx(ctx).Where(do.AdminUser{Username: in.Username}).Scan(&a); err != nil { + return nil, 0, gerror.Wrap(err, "query administrator") + } + if a.Id == 0 { + return nil, 0, response.Error(consts.CodeAdminNotFound, "administrator not found") + } + if a.Status != 1 || bcrypt.CompareHashAndPassword([]byte(a.PasswordHash), []byte(in.Password)) != nil { + return nil, 0, response.Error(consts.CodeAdminPasswordWrong, "username or password incorrect") + } + access, refresh, exp, err := s.tokens.Issue(a.Id, "admin", "") + if err != nil { + return nil, 0, gerror.Wrap(err, "issue token") + } + return &dto.TokenPair{AccessToken: access, RefreshToken: refresh, ExpiresIn: exp}, a.Id, nil +} +func (s *adminAuth) HasPermission(ctx context.Context, adminID uint64, permission string) (bool, error) { + // 多角色权限通过管理员-角色-菜单三表关联查询,菜单中的 permission 即接口权限标识。 + count, err := dao.AdminUserRole.Ctx(ctx).As("ur").LeftJoin("admin_role_menu rm", "ur.role_id=rm.role_id").LeftJoin("admin_menu m", "rm.menu_id=m.id").Where("ur.admin_user_id", adminID).Where("m.permission", permission).Where("m.status", 1).Count() + if err != nil { + return false, gerror.Wrap(err, "check permission") + } + return count > 0, nil +} diff --git a/internal/service/auth.go b/internal/service/auth.go new file mode 100644 index 0000000..aa3a68c --- /dev/null +++ b/internal/service/auth.go @@ -0,0 +1,51 @@ +package service + +import ( + "context" + "service.xpcool.com/internal/model/dto" +) + +type IUserAuth interface { + Login(context.Context, dto.UserLoginInput) (*dto.TokenPair, uint64, error) + Refresh(context.Context, string) (*dto.TokenPair, uint64, error) +} +type IAdminAuth interface { + Login(context.Context, dto.AdminLoginInput) (*dto.TokenPair, uint64, error) + HasPermission(context.Context, uint64, string) (bool, error) +} +type AuditEvent struct { + AdminID uint64 + Permission, Method, Path, IP, Param string + DurationMS, StatusCode int +} +type IAdminAudit interface { + Record(context.Context, AuditEvent) +} + +var ( + localUserAuth IUserAuth + localAdminAuth IAdminAuth + localAdminAudit IAdminAudit +) + +func UserAuth() IUserAuth { + if localUserAuth == nil { + panic("UserAuth implementation not registered") + } + return localUserAuth +} +func RegisterUserAuth(i IUserAuth) { localUserAuth = i } +func AdminAuth() IAdminAuth { + if localAdminAuth == nil { + panic("AdminAuth implementation not registered") + } + return localAdminAuth +} +func RegisterAdminAuth(i IAdminAuth) { localAdminAuth = i } +func AdminAudit() IAdminAudit { + if localAdminAudit == nil { + panic("AdminAudit implementation not registered") + } + return localAdminAudit +} +func RegisterAdminAudit(i IAdminAudit) { localAdminAudit = i } diff --git a/internal/service/user_auth.go b/internal/service/user_auth.go new file mode 100644 index 0000000..1a6771a --- /dev/null +++ b/internal/service/user_auth.go @@ -0,0 +1,131 @@ +package service + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gtime" + "golang.org/x/crypto/bcrypt" + "service.xpcool.com/internal/consts" + "service.xpcool.com/internal/dao" + "service.xpcool.com/internal/library/jwt" + "service.xpcool.com/internal/library/response" + "service.xpcool.com/internal/model/do" + "service.xpcool.com/internal/model/dto" + "service.xpcool.com/internal/model/entity" +) + +// WechatResolver 抽象微信 code 换 openid,避免业务层依赖具体 HTTP 实现。 +type WechatResolver interface { + OpenID(context.Context, string) (string, error) +} +type SMSVerifier interface { + Verify(context.Context, string, string) error +} + +// userAuth 负责用户三种登录方式及令牌签发,不承担第三方平台通信细节。 +type userAuth struct { + tokens *jwt.Service + wechat WechatResolver + sms SMSVerifier +} + +func NewUserAuth(tokens *jwt.Service, wechat WechatResolver, sms SMSVerifier) IUserAuth { + return &userAuth{tokens, wechat, sms} +} +func (s *userAuth) Login(ctx context.Context, in dto.UserLoginInput) (*dto.TokenPair, uint64, error) { + var u entity.User + var err error + switch in.LoginType { + case "wechat": + // 微信首次授权成功后按 openid 自动创建用户。 + if s.wechat == nil { + return nil, 0, response.Error(consts.CodeInternal, "wechat provider not configured") + } + openID, e := s.wechat.OpenID(ctx, in.Code) + if e != nil { + return nil, 0, gerror.Wrap(e, "wechat login") + } + err = dao.User.Ctx(ctx).Where(do.User{OpenId: openID}).Scan(&u) + if err != nil { + return nil, 0, gerror.Wrap(err, "query user") + } + if u.Id == 0 { + id, e := dao.User.Ctx(ctx).Data(do.User{OpenId: openID, Nickname: "微信用户", Status: 1}).InsertAndGetId() + if e != nil { + return nil, 0, gerror.Wrap(e, "create user") + } + u.Id = uint64(id) + } + case "mobile": + // 验证码由注入的服务校验,校验成功后按手机号自动注册。 + if s.sms == nil { + return nil, 0, response.Error(consts.CodeInternal, "sms provider not configured") + } + if err = s.sms.Verify(ctx, in.Mobile, in.VerifyCode); err != nil { + return nil, 0, response.Error(consts.CodeInvalidParam, "invalid verification code") + } + err = dao.User.Ctx(ctx).Where(do.User{Mobile: in.Mobile}).Scan(&u) + if err != nil { + return nil, 0, gerror.Wrap(err, "query user") + } + if u.Id == 0 { + id, e := dao.User.Ctx(ctx).Data(do.User{Mobile: in.Mobile, Nickname: "用户" + in.Mobile[7:], Status: 1}).InsertAndGetId() + if e != nil { + return nil, 0, gerror.Wrap(e, "create user") + } + u.Id = uint64(id) + } + case "password": + // 密码只使用 bcrypt 比对哈希值,任何场景都不回传或记录明文。 + err = dao.User.Ctx(ctx).Where(do.User{Account: in.Account}).Scan(&u) + if err != nil { + return nil, 0, gerror.Wrap(err, "query user") + } + if u.Id == 0 || bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(in.Password)) != nil { + return nil, 0, response.Error(consts.CodeUserPasswordWrong, "account or password incorrect") + } + default: + return nil, 0, response.Error(consts.CodeUserLoginType, "unsupported login type") + } + if u.Status != 1 { + return nil, 0, response.Error(consts.CodeForbidden, "user disabled") + } + _, err = dao.User.Ctx(ctx).Where(do.User{Id: u.Id}).Data(do.User{LastLoginAt: gtime.Now()}).Update() + if err != nil { + return nil, 0, gerror.Wrap(err, "update login time") + } + return s.issue(ctx, u.Id, in.Terminal) +} +func (s *userAuth) Refresh(ctx context.Context, refresh string) (*dto.TokenPair, uint64, error) { + // 刷新时先校验 JWT,再以 JTI 原子撤销旧会话,实现单次使用的令牌轮换。 + c, err := s.tokens.Parse(refresh, "refresh", "user") + if err != nil { + return nil, 0, response.Error(consts.CodeUnauthorized, "invalid refresh token") + } + result, err := dao.AuthRefreshSession.Ctx(ctx).Where(do.AuthRefreshSession{Jti: c.JTI}).WhereNull("revoked_at").Data(do.AuthRefreshSession{RevokedAt: gtime.Now()}).Update() + if err != nil { + return nil, 0, gerror.Wrap(err, "撤销旧刷新令牌失败") + } + affected, err := result.RowsAffected() + if err != nil || affected != 1 { + return nil, 0, response.Error(consts.CodeUnauthorized, "刷新令牌已失效") + } + return s.issue(ctx, c.Subject, c.Terminal) +} + +// issue 签发 JWT 后把 refresh token 的 JTI 落库,确保可以撤销和追踪设备会话。 +func (s *userAuth) issue(ctx context.Context, id uint64, terminal string) (*dto.TokenPair, uint64, error) { + a, r, exp, err := s.tokens.Issue(id, "user", terminal) + if err != nil { + return nil, 0, gerror.Wrap(err, "issue token") + } + claims, err := s.tokens.Parse(r, "refresh", "user") + if err != nil { + return nil, 0, gerror.Wrap(err, "解析新刷新令牌失败") + } + _, err = dao.AuthRefreshSession.Ctx(ctx).Data(do.AuthRefreshSession{SubjectId: id, Scope: "user", Terminal: terminal, Jti: claims.JTI, ExpiredAt: gtime.NewFromTimeStamp(claims.ExpireAt)}).Insert() + if err != nil { + return nil, 0, gerror.Wrap(err, "保存刷新令牌会话失败") + } + return &dto.TokenPair{AccessToken: a, RefreshToken: r, ExpiresIn: exp}, id, nil +} diff --git a/internal/table/admin_menu.go b/internal/table/admin_menu.go new file mode 100644 index 0000000..8d8cc81 --- /dev/null +++ b/internal/table/admin_menu.go @@ -0,0 +1,133 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminMenu defines the fields of table "admin_menu" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AdminMenu = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "parent_id": { + Index: 1, + Name: "parent_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: "0", + Extra: "", + Comment: "", + }, + "name": { + Index: 2, + Name: "name", + Type: "varchar(64)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "type": { + Index: 3, + Name: "type", + Type: "tinyint", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "1 menu,2 api", + }, + "path": { + Index: 4, + Name: "path", + Type: "varchar(255)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "permission": { + Index: 5, + Name: "permission", + Type: "varchar(128)", + Null: false, + Key: "UNI", + Default: "", + Extra: "", + Comment: "", + }, + "sort": { + Index: 6, + Name: "sort", + Type: "int", + Null: false, + Key: "", + Default: "0", + Extra: "", + Comment: "", + }, + "status": { + Index: 7, + Name: "status", + Type: "tinyint", + Null: false, + Key: "", + Default: "1", + Extra: "", + Comment: "", + }, + "created_at": { + Index: 8, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 9, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 10, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAdminMenuTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAdminMenuTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_menu", AdminMenu, schema...) +} diff --git a/internal/table/admin_operation_log.go b/internal/table/admin_operation_log.go new file mode 100644 index 0000000..6fba817 --- /dev/null +++ b/internal/table/admin_operation_log.go @@ -0,0 +1,143 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminOperationLog defines the fields of table "admin_operation_log" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AdminOperationLog = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "admin_user_id": { + Index: 1, + Name: "admin_user_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "permission": { + Index: 2, + Name: "permission", + Type: "varchar(128)", + Null: false, + Key: "MUL", + Default: "", + Extra: "", + Comment: "", + }, + "method": { + Index: 3, + Name: "method", + Type: "varchar(12)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "path": { + Index: 4, + Name: "path", + Type: "varchar(255)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "ip": { + Index: 5, + Name: "ip", + Type: "varchar(64)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "request_param": { + Index: 6, + Name: "request_param", + Type: "json", + Null: true, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "duration_ms": { + Index: 7, + Name: "duration_ms", + Type: "int unsigned", + Null: false, + Key: "", + Default: "0", + Extra: "", + Comment: "", + }, + "status_code": { + Index: 8, + Name: "status_code", + Type: "int", + Null: false, + Key: "", + Default: "0", + Extra: "", + Comment: "", + }, + "created_at": { + Index: 9, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 10, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 11, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAdminOperationLogTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAdminOperationLogTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_operation_log", AdminOperationLog, schema...) +} diff --git a/internal/table/admin_role.go b/internal/table/admin_role.go new file mode 100644 index 0000000..b957b00 --- /dev/null +++ b/internal/table/admin_role.go @@ -0,0 +1,93 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminRole defines the fields of table "admin_role" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AdminRole = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "code": { + Index: 1, + Name: "code", + Type: "varchar(64)", + Null: false, + Key: "UNI", + Default: nil, + Extra: "", + Comment: "", + }, + "name": { + Index: 2, + Name: "name", + Type: "varchar(64)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "status": { + Index: 3, + Name: "status", + Type: "tinyint", + Null: false, + Key: "", + Default: "1", + Extra: "", + Comment: "", + }, + "created_at": { + Index: 4, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 5, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 6, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAdminRoleTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAdminRoleTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_role", AdminRole, schema...) +} diff --git a/internal/table/admin_role_menu.go b/internal/table/admin_role_menu.go new file mode 100644 index 0000000..17eafa1 --- /dev/null +++ b/internal/table/admin_role_menu.go @@ -0,0 +1,83 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminRoleMenu defines the fields of table "admin_role_menu" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AdminRoleMenu = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "role_id": { + Index: 1, + Name: "role_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "menu_id": { + Index: 2, + Name: "menu_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "created_at": { + Index: 3, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 4, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 5, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAdminRoleMenuTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAdminRoleMenuTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_role_menu", AdminRoleMenu, schema...) +} diff --git a/internal/table/admin_user.go b/internal/table/admin_user.go new file mode 100644 index 0000000..49b4064 --- /dev/null +++ b/internal/table/admin_user.go @@ -0,0 +1,113 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminUser defines the fields of table "admin_user" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AdminUser = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "username": { + Index: 1, + Name: "username", + Type: "varchar(64)", + Null: false, + Key: "UNI", + Default: nil, + Extra: "", + Comment: "", + }, + "password_hash": { + Index: 2, + Name: "password_hash", + Type: "varchar(100)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "nickname": { + Index: 3, + Name: "nickname", + Type: "varchar(64)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "status": { + Index: 4, + Name: "status", + Type: "tinyint", + Null: false, + Key: "MUL", + Default: "1", + Extra: "", + Comment: "", + }, + "last_login_at": { + Index: 5, + Name: "last_login_at", + Type: "datetime", + Null: true, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "created_at": { + Index: 6, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 7, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 8, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAdminUserTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAdminUserTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_user", AdminUser, schema...) +} diff --git a/internal/table/admin_user_role.go b/internal/table/admin_user_role.go new file mode 100644 index 0000000..c3231dc --- /dev/null +++ b/internal/table/admin_user_role.go @@ -0,0 +1,83 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AdminUserRole defines the fields of table "admin_user_role" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AdminUserRole = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "admin_user_id": { + Index: 1, + Name: "admin_user_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "role_id": { + Index: 2, + Name: "role_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "created_at": { + Index: 3, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 4, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 5, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAdminUserRoleTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAdminUserRoleTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "admin_user_role", AdminUserRole, schema...) +} diff --git a/internal/table/auth_refresh_session.go b/internal/table/auth_refresh_session.go new file mode 100644 index 0000000..60ce418 --- /dev/null +++ b/internal/table/auth_refresh_session.go @@ -0,0 +1,123 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// AuthRefreshSession defines the fields of table "auth_refresh_session" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var AuthRefreshSession = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "subject_id": { + Index: 1, + Name: "subject_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "????????? ID", + }, + "scope": { + Index: 2, + Name: "scope", + Type: "varchar(16)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "user/admin", + }, + "terminal": { + Index: 3, + Name: "terminal", + Type: "varchar(16)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "mini/h5/app", + }, + "jti": { + Index: 4, + Name: "jti", + Type: "char(43)", + Null: false, + Key: "UNI", + Default: nil, + Extra: "", + Comment: "JWT ??????", + }, + "expired_at": { + Index: 5, + Name: "expired_at", + Type: "datetime", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "revoked_at": { + Index: 6, + Name: "revoked_at", + Type: "datetime", + Null: true, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "created_at": { + Index: 7, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 8, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 9, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetAuthRefreshSessionTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetAuthRefreshSessionTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "auth_refresh_session", AuthRefreshSession, schema...) +} diff --git a/internal/table/content.go b/internal/table/content.go new file mode 100644 index 0000000..d068387 --- /dev/null +++ b/internal/table/content.go @@ -0,0 +1,93 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// Content defines the fields of table "content" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var Content = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "title": { + Index: 1, + Name: "title", + Type: "varchar(200)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "body": { + Index: 2, + Name: "body", + Type: "longtext", + Null: true, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "status": { + Index: 3, + Name: "status", + Type: "tinyint", + Null: false, + Key: "MUL", + Default: "1", + Extra: "", + Comment: "", + }, + "created_at": { + Index: 4, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 5, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 6, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetContentTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetContentTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "content", Content, schema...) +} diff --git a/internal/table/user.go b/internal/table/user.go new file mode 100644 index 0000000..86f9f42 --- /dev/null +++ b/internal/table/user.go @@ -0,0 +1,153 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// User defines the fields of table "user" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var User = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "union_id": { + Index: 1, + Name: "union_id", + Type: "varchar(64)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "open_id": { + Index: 2, + Name: "open_id", + Type: "varchar(64)", + Null: false, + Key: "UNI", + Default: "", + Extra: "", + Comment: "", + }, + "mobile": { + Index: 3, + Name: "mobile", + Type: "varchar(20)", + Null: false, + Key: "UNI", + Default: "", + Extra: "", + Comment: "", + }, + "account": { + Index: 4, + Name: "account", + Type: "varchar(64)", + Null: false, + Key: "UNI", + Default: "", + Extra: "", + Comment: "", + }, + "password_hash": { + Index: 5, + Name: "password_hash", + Type: "varchar(100)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "nickname": { + Index: 6, + Name: "nickname", + Type: "varchar(64)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "avatar": { + Index: 7, + Name: "avatar", + Type: "varchar(512)", + Null: false, + Key: "", + Default: "", + Extra: "", + Comment: "", + }, + "status": { + Index: 8, + Name: "status", + Type: "tinyint", + Null: false, + Key: "MUL", + Default: "1", + Extra: "", + Comment: "", + }, + "last_login_at": { + Index: 9, + Name: "last_login_at", + Type: "datetime", + Null: true, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "created_at": { + Index: 10, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 11, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 12, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetUserTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetUserTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "user", User, schema...) +} diff --git a/internal/table/user_favorite.go b/internal/table/user_favorite.go new file mode 100644 index 0000000..67c6146 --- /dev/null +++ b/internal/table/user_favorite.go @@ -0,0 +1,83 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// UserFavorite defines the fields of table "user_favorite" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var UserFavorite = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "user_id": { + Index: 1, + Name: "user_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "content_id": { + Index: 2, + Name: "content_id", + Type: "bigint unsigned", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "created_at": { + Index: 3, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 4, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 5, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetUserFavoriteTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetUserFavoriteTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "user_favorite", UserFavorite, schema...) +} diff --git a/internal/table/user_message.go b/internal/table/user_message.go new file mode 100644 index 0000000..d0376ed --- /dev/null +++ b/internal/table/user_message.go @@ -0,0 +1,103 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package table + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" +) + +// UserMessage defines the fields of table "user_message" with their properties. +// This map is used internally by GoFrame ORM to understand table structure. +var UserMessage = map[string]*gdb.TableField{ + "id": { + Index: 0, + Name: "id", + Type: "bigint unsigned", + Null: false, + Key: "PRI", + Default: nil, + Extra: "auto_increment", + Comment: "", + }, + "user_id": { + Index: 1, + Name: "user_id", + Type: "bigint unsigned", + Null: false, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, + "title": { + Index: 2, + Name: "title", + Type: "varchar(200)", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "content": { + Index: 3, + Name: "content", + Type: "text", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "is_read": { + Index: 4, + Name: "is_read", + Type: "tinyint", + Null: false, + Key: "", + Default: "0", + Extra: "", + Comment: "", + }, + "created_at": { + Index: 5, + Name: "created_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "updated_at": { + Index: 6, + Name: "updated_at", + Type: "datetime", + Null: false, + Key: "", + Default: nil, + Extra: "", + Comment: "", + }, + "deleted_at": { + Index: 7, + Name: "deleted_at", + Type: "datetime", + Null: true, + Key: "MUL", + Default: nil, + Extra: "", + Comment: "", + }, +} + +// SetUserMessageTableFields registers the table fields definition to the database instance. +// db: database instance that implements gdb.DB interface. +// schema: optional schema/namespace name, especially for databases that support schemas. +func SetUserMessageTableFields(ctx context.Context, db gdb.DB, schema ...string) error { + return db.GetCore().SetTableFields(ctx, "user_message", UserMessage, schema...) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..df43a1a --- /dev/null +++ b/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "github.com/gogf/gf/v2/os/gctx" + + "service.xpcool.com/internal/cmd" +) + +func main() { + cmd.Main.Run(gctx.GetInitCtx()) +} diff --git a/manifest/config/config.dev.yaml b/manifest/config/config.dev.yaml new file mode 100644 index 0000000..39c4469 --- /dev/null +++ b/manifest/config/config.dev.yaml @@ -0,0 +1,13 @@ +server: + address: ":8000" + openapiPath: "/api.json" + swaggerPath: "/swagger" +logger: { level: "all", stdout: true } +database: + default: + link: "${DB_DSN}" +jwt: + # Must be overridden by JWT_SECRET in every deployed environment. + secret: "${JWT_SECRET}" + accessExpire: "2h" + refreshExpire: "720h" diff --git a/manifest/config/config.prod.yaml b/manifest/config/config.prod.yaml new file mode 100644 index 0000000..6a2bbdc --- /dev/null +++ b/manifest/config/config.prod.yaml @@ -0,0 +1,4 @@ +server: { address: ":8000", openapiPath: "/api.json", swaggerPath: "/swagger" } +logger: { level: "warning", stdout: true } +database: { default: { link: "${DB_DSN}" } } +jwt: { secret: "${JWT_SECRET}", accessExpire: "2h", refreshExpire: "720h" } diff --git a/manifest/config/config.test.yaml b/manifest/config/config.test.yaml new file mode 100644 index 0000000..6a9181d --- /dev/null +++ b/manifest/config/config.test.yaml @@ -0,0 +1,3 @@ +server: { address: ":8001" } +database: { default: { link: "${TEST_DB_DSN}" } } +jwt: { secret: "${JWT_SECRET}", accessExpire: "15m", refreshExpire: "1h" } diff --git a/manifest/deploy/kustomize/base/deployment.yaml b/manifest/deploy/kustomize/base/deployment.yaml new file mode 100644 index 0000000..28f1d69 --- /dev/null +++ b/manifest/deploy/kustomize/base/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-single + labels: + app: template-single +spec: + replicas: 1 + selector: + matchLabels: + app: template-single + template: + metadata: + labels: + app: template-single + spec: + containers: + - name : main + image: template-single + imagePullPolicy: Always + diff --git a/manifest/deploy/kustomize/base/kustomization.yaml b/manifest/deploy/kustomize/base/kustomization.yaml new file mode 100644 index 0000000..302d92d --- /dev/null +++ b/manifest/deploy/kustomize/base/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +- service.yaml + + + diff --git a/manifest/deploy/kustomize/base/service.yaml b/manifest/deploy/kustomize/base/service.yaml new file mode 100644 index 0000000..608771c --- /dev/null +++ b/manifest/deploy/kustomize/base/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: template-single +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 8000 + selector: + app: template-single + diff --git a/manifest/deploy/kustomize/overlays/develop/configmap.yaml b/manifest/deploy/kustomize/overlays/develop/configmap.yaml new file mode 100644 index 0000000..3b1d0af --- /dev/null +++ b/manifest/deploy/kustomize/overlays/develop/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: template-single-configmap +data: + config.yaml: | + server: + address: ":8000" + openapiPath: "/api.json" + swaggerPath: "/swagger" + + logger: + level : "all" + stdout: true diff --git a/manifest/deploy/kustomize/overlays/develop/deployment.yaml b/manifest/deploy/kustomize/overlays/develop/deployment.yaml new file mode 100644 index 0000000..04e4851 --- /dev/null +++ b/manifest/deploy/kustomize/overlays/develop/deployment.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-single +spec: + template: + spec: + containers: + - name : main + image: template-single:develop \ No newline at end of file diff --git a/manifest/deploy/kustomize/overlays/develop/kustomization.yaml b/manifest/deploy/kustomize/overlays/develop/kustomization.yaml new file mode 100644 index 0000000..4731c47 --- /dev/null +++ b/manifest/deploy/kustomize/overlays/develop/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../base +- configmap.yaml + +patchesStrategicMerge: +- deployment.yaml + +namespace: default + + + diff --git a/manifest/docker/Dockerfile b/manifest/docker/Dockerfile new file mode 100644 index 0000000..d3abe8f --- /dev/null +++ b/manifest/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM loads/alpine:3.8 + +############################################################################### +# INSTALLATION +############################################################################### + +ENV WORKDIR /app +ADD resource $WORKDIR/ +ADD ./temp/linux_amd64/main $WORKDIR/main +RUN chmod +x $WORKDIR/main + +############################################################################### +# START +############################################################################### +WORKDIR $WORKDIR +CMD ./main diff --git a/manifest/docker/docker.sh b/manifest/docker/docker.sh new file mode 100644 index 0000000..ff393f9 --- /dev/null +++ b/manifest/docker/docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This shell is executed before docker build. + + + + + diff --git a/manifest/i18n/.gitkeep b/manifest/i18n/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/manifest/protobuf/.keep-if-necessary b/manifest/protobuf/.keep-if-necessary new file mode 100644 index 0000000..e69de29 diff --git a/manifest/sql/001_core.sql b/manifest/sql/001_core.sql new file mode 100644 index 0000000..3e0a066 --- /dev/null +++ b/manifest/sql/001_core.sql @@ -0,0 +1,29 @@ +-- Core schema. All business tables include deleted_at for GoFrame automatic soft deletion. +CREATE TABLE IF NOT EXISTS user ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, union_id VARCHAR(64) NOT NULL DEFAULT '', open_id VARCHAR(64) NOT NULL DEFAULT '', mobile VARCHAR(20) NOT NULL DEFAULT '', account VARCHAR(64) NOT NULL DEFAULT '', password_hash VARCHAR(100) NOT NULL DEFAULT '', nickname VARCHAR(64) NOT NULL DEFAULT '', avatar VARCHAR(512) NOT NULL DEFAULT '', status TINYINT NOT NULL DEFAULT 1, last_login_at DATETIME NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_user_open_id (open_id), UNIQUE KEY uk_user_mobile (mobile), UNIQUE KEY uk_user_account (account), KEY idx_user_status_created (status, created_at), KEY idx_user_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='C-end user'; +CREATE TABLE IF NOT EXISTS admin_user ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(64) NOT NULL, password_hash VARCHAR(100) NOT NULL, nickname VARCHAR(64) NOT NULL DEFAULT '', status TINYINT NOT NULL DEFAULT 1, last_login_at DATETIME NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_admin_username (username), KEY idx_admin_status (status), KEY idx_admin_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Admin user'; +CREATE TABLE IF NOT EXISTS admin_role ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, code VARCHAR(64) NOT NULL, name VARCHAR(64) NOT NULL, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_role_code (code), KEY idx_role_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='RBAC role'; +CREATE TABLE IF NOT EXISTS admin_menu ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, parent_id BIGINT UNSIGNED NOT NULL DEFAULT 0, name VARCHAR(64) NOT NULL, type TINYINT NOT NULL COMMENT '1 menu,2 api', path VARCHAR(255) NOT NULL DEFAULT '', permission VARCHAR(128) NOT NULL DEFAULT '', sort INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_menu_permission (permission), KEY idx_menu_parent_sort (parent_id,sort), KEY idx_menu_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Menu and API permission'; +CREATE TABLE IF NOT EXISTS admin_user_role ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, admin_user_id BIGINT UNSIGNED NOT NULL, role_id BIGINT UNSIGNED NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_user_role (admin_user_id,role_id), KEY idx_aur_role (role_id), KEY idx_aur_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Admin-role relation'; +CREATE TABLE IF NOT EXISTS admin_role_menu ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, role_id BIGINT UNSIGNED NOT NULL, menu_id BIGINT UNSIGNED NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_role_menu (role_id,menu_id), KEY idx_arm_menu (menu_id), KEY idx_arm_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Role-menu relation'; +CREATE TABLE IF NOT EXISTS admin_operation_log ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, admin_user_id BIGINT UNSIGNED NOT NULL, permission VARCHAR(128) NOT NULL DEFAULT '', method VARCHAR(12) NOT NULL, path VARCHAR(255) NOT NULL, ip VARCHAR(64) NOT NULL DEFAULT '', request_param JSON NULL, duration_ms INT UNSIGNED NOT NULL DEFAULT 0, status_code INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, deleted_at DATETIME NULL, + PRIMARY KEY (id), KEY idx_aol_user_created (admin_user_id,created_at), KEY idx_aol_permission_created (permission,created_at), KEY idx_aol_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Admin operation audit log'; diff --git a/manifest/sql/002_business_extension.sql b/manifest/sql/002_business_extension.sql new file mode 100644 index 0000000..92b356d --- /dev/null +++ b/manifest/sql/002_business_extension.sql @@ -0,0 +1,52 @@ +-- 刷新令牌会话表:实现令牌轮换、登出和改密后的主动失效。 + +CREATE TABLE IF NOT EXISTS auth_refresh_session ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + subject_id BIGINT UNSIGNED NOT NULL COMMENT '用户或管理员 ID', + scope VARCHAR(16) NOT NULL COMMENT 'user/admin', + terminal VARCHAR(16) NOT NULL DEFAULT '' COMMENT 'mini/h5/app', + jti CHAR(43) NOT NULL COMMENT 'JWT 唯一编号', + expired_at DATETIME NOT NULL, + revoked_at DATETIME NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), + UNIQUE KEY uk_refresh_jti (jti), + KEY idx_refresh_subject_scope (subject_id, scope), + KEY idx_refresh_expired_at (expired_at), + KEY idx_refresh_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='刷新令牌会话'; + +-- 内容、收藏、站内消息是用户端基础模块的最小数据模型。 + +CREATE TABLE IF NOT EXISTS content ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + title VARCHAR(200) NOT NULL, + body LONGTEXT NULL, + status TINYINT NOT NULL DEFAULT 1, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), KEY idx_content_status_created (status, created_at), KEY idx_content_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通用内容'; +CREATE TABLE IF NOT EXISTS user_favorite ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + user_id BIGINT UNSIGNED NOT NULL, + content_id BIGINT UNSIGNED NOT NULL, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), UNIQUE KEY uk_favorite_user_content (user_id, content_id), KEY idx_favorite_user_created (user_id, created_at), KEY idx_favorite_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户收藏'; +CREATE TABLE IF NOT EXISTS user_message ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + user_id BIGINT UNSIGNED NOT NULL, + title VARCHAR(200) NOT NULL, + content TEXT NOT NULL, + is_read TINYINT NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + deleted_at DATETIME NULL, + PRIMARY KEY (id), KEY idx_message_user_read_created (user_id, is_read, created_at), KEY idx_message_deleted_at (deleted_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户站内消息'; diff --git a/resource/public/html/.gitkeep b/resource/public/html/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/public/plugin/.gitkeep b/resource/public/plugin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/public/resource/css/.gitkeep b/resource/public/resource/css/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/public/resource/image/.gitkeep b/resource/public/resource/image/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/public/resource/js/.gitkeep b/resource/public/resource/js/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resource/template/.gitkeep b/resource/template/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/utility/.gitkeep b/utility/.gitkeep new file mode 100644 index 0000000..e69de29