├── server ├── bin │ └── .gitkeep ├── uploadFile │ └── .gitkeep ├── repositories │ └── modules │ │ ├── auth │ │ └── auth.go │ │ ├── commits │ │ └── commits.go │ │ ├── rolesAndInterfaces │ │ └── rolesAndInterfaces.go │ │ ├── repositories.go │ │ ├── logs │ │ └── logs.go │ │ └── rolesAndPages │ │ └── roleAndPages.go ├── utils │ ├── modules │ │ ├── contain │ │ │ └── contain.go │ │ ├── unique │ │ │ └── unique.go │ │ ├── bcrypt │ │ │ └── bcrypt.go │ │ ├── logs │ │ │ └── logs.go │ │ ├── snowflake │ │ │ └── snowflake.go │ │ ├── file │ │ │ └── file.go │ │ ├── translator │ │ │ └── translator.go │ │ ├── exportExcel │ │ │ └── exportExcel.go │ │ ├── jwt │ │ │ └── jwt.go │ │ ├── timeTask │ │ │ └── timeTask.go │ │ └── captcha │ │ │ └── captcha.go │ └── utils.go ├── dto │ └── modules │ │ ├── common │ │ └── common.go │ │ ├── commits │ │ └── commits.go │ │ ├── logs │ │ └── logs.go │ │ ├── auth │ │ └── auth.go │ │ ├── upload │ │ └── upload.go │ │ ├── timeTask │ │ └── timeTask.go │ │ ├── department │ │ └── department.go │ │ ├── roles │ │ └── roles.go │ │ ├── interface │ │ └── interface.go │ │ ├── pages │ │ └── pages.go │ │ └── template │ │ └── template.go ├── routers │ └── modules │ │ ├── logs │ │ └── logs.go │ │ ├── system │ │ └── system.go │ │ ├── commits │ │ └── commits.go │ │ ├── template │ │ └── template.go │ │ ├── auth │ │ └── auth.go │ │ ├── department │ │ └── department.go │ │ ├── swagger │ │ └── swagger.go │ │ ├── timeTask │ │ └── timeTask.go │ │ ├── pages │ │ └── pages.go │ │ ├── roles │ │ └── roles.go │ │ ├── interface │ │ └── interface.go │ │ ├── users │ │ └── users.go │ │ └── upload │ │ └── upload.go ├── main.go ├── services │ ├── modules │ │ ├── logs │ │ │ └── logs.go │ │ ├── rolesAndInterfaces │ │ │ └── rolesAndInterfaces.go │ │ ├── usersAndRoles │ │ │ └── usersAndRoles.go │ │ ├── department │ │ │ └── department.go │ │ └── interface │ │ │ └── interface.go │ └── service.go ├── template │ └── server │ │ ├── route │ │ ├── route.go │ │ └── route.tmpl │ │ ├── dto │ │ ├── dto.go │ │ └── dto.tmpl │ │ └── service │ │ ├── service.go │ │ └── service.tmpl ├── middlewares │ ├── modules │ │ ├── session │ │ │ └── session.go │ │ ├── request │ │ │ └── log.go │ │ ├── cors │ │ │ └── cors.go │ │ └── logs │ │ │ └── logs.go │ └── middlewares.go ├── bootStrap │ └── bootStrap.go ├── db │ └── db.go ├── config │ └── config.go └── controllers │ ├── modules │ ├── logs │ │ └── logs.go │ ├── commits │ │ └── commits.go │ └── system │ │ └── system.go │ └── controllers.go ├── micro ├── .dockerignore ├── sql │ ├── data_master │ │ └── .gitkeep │ ├── data_slave │ │ └── .gitkeep │ ├── master.cnf │ ├── slave.cnf │ ├── init-master.sh │ └── init-slave.sh ├── README.md ├── config │ └── config.go ├── rpc │ ├── role │ │ ├── etc │ │ │ └── role.yaml │ │ ├── internal │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── svc │ │ │ │ └── servicecontext.go │ │ │ └── logic │ │ │ │ ├── createrolelogic.go │ │ │ │ ├── roleidshasbeenexistlogic.go │ │ │ │ ├── rolenamehasbeenexistlogic.go │ │ │ │ ├── deleterolelogic.go │ │ │ │ ├── updaterolelogic.go │ │ │ │ ├── getrolelogic.go │ │ │ │ └── getrolelistlogic.go │ │ ├── Dockerfile │ │ ├── role.go │ │ └── desc │ │ │ └── role.proto │ ├── user │ │ ├── etc │ │ │ └── user.yaml │ │ ├── internal │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── svc │ │ │ │ └── servicecontext.go │ │ │ └── logic │ │ │ │ ├── useridhasbeenexistlogic.go │ │ │ │ ├── useraccounthasbeenexistlogic.go │ │ │ │ ├── getuserlogic.go │ │ │ │ ├── getuserbyaccountlogic.go │ │ │ │ ├── deleteuserlogic.go │ │ │ │ ├── updateuserlogic.go │ │ │ │ ├── createuserlogic.go │ │ │ │ └── getuserlistlogic.go │ │ ├── Dockerfile │ │ └── user.go │ ├── captcha │ │ ├── etc │ │ │ └── captcha.yaml │ │ ├── internal │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── svc │ │ │ │ └── servicecontext.go │ │ │ ├── server │ │ │ │ └── captchaserviceserver.go │ │ │ └── logic │ │ │ │ ├── verifycaptchalogic.go │ │ │ │ └── generatecaptchalogic.go │ │ ├── Dockerfile │ │ ├── desc │ │ │ └── captcha.proto │ │ ├── captcha.go │ │ └── captchaservice │ │ │ └── captchaservice.go │ └── auth │ │ └── desc │ │ └── auth.proto ├── api │ ├── test │ │ ├── etc │ │ │ └── user-api.yaml │ │ ├── internal │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── svc │ │ │ │ └── servicecontext.go │ │ │ ├── types │ │ │ │ └── types.go │ │ │ ├── handler │ │ │ │ ├── routes.go │ │ │ │ ├── loginhandler.go │ │ │ │ └── userinfohandler.go │ │ │ └── logic │ │ │ │ ├── userinfologic.go │ │ │ │ └── loginlogic.go │ │ ├── desc │ │ │ └── test.api │ │ └── user.go │ ├── user │ │ ├── etc │ │ │ └── userservice.yaml │ │ ├── internal │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── svc │ │ │ │ └── servicecontext.go │ │ │ ├── handler │ │ │ │ ├── createuserhandler.go │ │ │ │ ├── deleteuserhandler.go │ │ │ │ ├── updateuserhandler.go │ │ │ │ ├── getuserlisthandler.go │ │ │ │ └── routes.go │ │ │ └── logic │ │ │ │ ├── deleteuserlogic.go │ │ │ │ ├── updateuserlogic.go │ │ │ │ ├── createuserlogic.go │ │ │ │ └── getuserlistlogic.go │ │ ├── Dockerfile │ │ └── userservice.go │ └── auth │ │ ├── etc │ │ └── auth.yaml │ │ ├── internal │ │ ├── config │ │ │ └── config.go │ │ ├── types │ │ │ └── types.go │ │ ├── handler │ │ │ ├── routes.go │ │ │ ├── loginhandler.go │ │ │ └── captchahandler.go │ │ ├── svc │ │ │ └── servicecontext.go │ │ └── logic │ │ │ └── captchalogic.go │ │ ├── Dockerfile │ │ ├── auth.go │ │ └── desc │ │ └── auth.api ├── model │ ├── role │ │ └── role.go │ └── user │ │ └── user.go ├── shared │ ├── redis │ │ └── redis.go │ ├── snowflake │ │ └── snowflake.go │ ├── token │ │ └── token.go │ └── gorm │ │ └── gorm.go └── nginx.conf ├── web ├── .env.dev ├── .env.pro ├── src │ ├── types │ │ ├── index.ts │ │ └── menus │ │ │ └── index.ts │ ├── styles │ │ ├── index.css │ │ ├── global │ │ │ └── index.css │ │ └── common │ │ │ └── index.css │ ├── assets │ │ ├── image │ │ │ ├── 404.png │ │ │ ├── logo.png │ │ │ └── loading.gif │ │ └── svg │ │ │ ├── moon.svg │ │ │ ├── start.svg │ │ │ ├── stop.svg │ │ │ ├── en-zh.svg │ │ │ └── zh-en.svg │ ├── utils │ │ ├── sleep │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── cache │ │ │ └── index.ts │ │ ├── echarts │ │ │ └── index.ts │ │ ├── format │ │ │ └── index.ts │ │ ├── event │ │ │ └── index.ts │ │ └── message │ │ │ └── index.tsx │ ├── pages │ │ ├── Login │ │ │ └── type.ts │ │ └── NotFond │ │ │ └── index.tsx │ ├── components │ │ ├── Icon │ │ │ ├── dark.tsx │ │ │ ├── light.tsx │ │ │ ├── translateDark.tsx │ │ │ ├── translateLight.tsx │ │ │ └── icon.tsx │ │ ├── Iframe │ │ │ └── index.tsx │ │ ├── Theme │ │ │ └── index.tsx │ │ ├── Card │ │ │ └── index.tsx │ │ ├── Footer │ │ │ └── index.tsx │ │ ├── Auth │ │ │ └── index.tsx │ │ ├── AppBreadcrumb │ │ │ └── index.tsx │ │ ├── IconSelect │ │ │ └── index.tsx │ │ ├── index.ts │ │ ├── Translate │ │ │ └── translate.tsx │ │ └── AppHeaderTab │ │ │ └── index.tsx │ ├── service │ │ ├── index.ts │ │ ├── api │ │ │ ├── logs │ │ │ │ └── index.ts │ │ │ ├── login │ │ │ │ └── index.ts │ │ │ ├── timeTask │ │ │ │ └── index.ts │ │ │ ├── pages │ │ │ │ └── index.ts │ │ │ ├── department │ │ │ │ └── index.ts │ │ │ ├── interface │ │ │ │ └── index.ts │ │ │ ├── template │ │ │ │ └── index.ts │ │ │ ├── file │ │ │ │ └── index.ts │ │ │ └── system │ │ │ │ └── index.ts │ │ └── request │ │ │ ├── lib │ │ │ └── type.ts │ │ │ └── index.ts │ ├── hooks │ │ ├── useTheme.ts │ │ └── useAppRoutes.tsx │ ├── vite-env.d.ts │ ├── router │ │ └── index.tsx │ ├── local │ │ └── index.ts │ ├── store │ │ ├── UploadStore │ │ │ └── index.ts │ │ ├── UserStore │ │ │ └── index.ts │ │ └── index.ts │ ├── main.tsx │ ├── App.tsx │ └── constant │ │ └── index.ts ├── postcss.config.js ├── .prettierrc ├── tailwind.config.ts ├── tsconfig.node.json ├── index.html ├── .gitignore ├── tsconfig.json └── vite.config.ts ├── .gitignore ├── static ├── login.png ├── system.png └── dashboard.png ├── Dockerfile ├── .github └── workflows │ └── go.yml └── README.md /server/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/uploadFile/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /micro/.dockerignore: -------------------------------------------------------------------------------- 1 | sql/ 2 | .git/ -------------------------------------------------------------------------------- /micro/sql/data_master/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /micro/sql/data_slave/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/.env.dev: -------------------------------------------------------------------------------- 1 | VITE_APP_BASE_URL = "/cms" -------------------------------------------------------------------------------- /web/.env.pro: -------------------------------------------------------------------------------- 1 | VITE_APP_BASE_URL = "/cms" -------------------------------------------------------------------------------- /micro/README.md: -------------------------------------------------------------------------------- 1 | # 微服务版本 2 | > 开发中 Coding... -------------------------------------------------------------------------------- /web/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type * from './menus/index'; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /micro/sql/data_master/ 2 | /micro/sql/data_slave/ 3 | 4 | 5 | -------------------------------------------------------------------------------- /micro/config/config.go: -------------------------------------------------------------------------------- 1 | package appConfig 2 | 3 | var RedisHost = "redis:6379" 4 | -------------------------------------------------------------------------------- /static/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xi-Yuer/GO-CMS/HEAD/static/login.png -------------------------------------------------------------------------------- /micro/sql/master.cnf: -------------------------------------------------------------------------------- 1 | server-id=1 2 | log-bin=mysql-bin 3 | binlog-do-db=micro_cms 4 | -------------------------------------------------------------------------------- /static/system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xi-Yuer/GO-CMS/HEAD/static/system.png -------------------------------------------------------------------------------- /static/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xi-Yuer/GO-CMS/HEAD/static/dashboard.png -------------------------------------------------------------------------------- /web/src/styles/index.css: -------------------------------------------------------------------------------- 1 | @import './common/index.css'; 2 | @import './global/index.css'; 3 | -------------------------------------------------------------------------------- /web/src/assets/image/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xi-Yuer/GO-CMS/HEAD/web/src/assets/image/404.png -------------------------------------------------------------------------------- /web/src/assets/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xi-Yuer/GO-CMS/HEAD/web/src/assets/image/logo.png -------------------------------------------------------------------------------- /web/src/assets/image/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xi-Yuer/GO-CMS/HEAD/web/src/assets/image/loading.gif -------------------------------------------------------------------------------- /micro/sql/slave.cnf: -------------------------------------------------------------------------------- 1 | server-id=2 2 | relay-log=relay-log 3 | log-bin=mysql-bin 4 | read-only=1 5 | binlog-do-db=micro_cms -------------------------------------------------------------------------------- /web/src/utils/sleep/index.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); 2 | -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /micro/rpc/role/etc/role.yaml: -------------------------------------------------------------------------------- 1 | Name: role.rpc 2 | ListenOn: 0.0.0.0:8082 3 | Etcd: 4 | Hosts: 5 | - etcd:2379 6 | Key: role.rpc 7 | -------------------------------------------------------------------------------- /micro/rpc/user/etc/user.yaml: -------------------------------------------------------------------------------- 1 | Name: user.rpc 2 | ListenOn: 0.0.0.0:8083 3 | Etcd: 4 | Hosts: 5 | - etcd:2379 6 | Key: user.rpc 7 | -------------------------------------------------------------------------------- /web/src/pages/Login/type.ts: -------------------------------------------------------------------------------- 1 | export type FieldType = { 2 | account?: string; 3 | password?: string; 4 | captcha?: string; 5 | }; 6 | -------------------------------------------------------------------------------- /web/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cache'; 2 | export * from './sleep'; 3 | export * from './builder'; 4 | export * from './message'; 5 | -------------------------------------------------------------------------------- /micro/rpc/captcha/etc/captcha.yaml: -------------------------------------------------------------------------------- 1 | Name: captcha.rpc 2 | ListenOn: 0.0.0.0:8081 3 | Etcd: 4 | Hosts: 5 | - etcd:2379 6 | Key: captcha.rpc 7 | -------------------------------------------------------------------------------- /micro/api/test/etc/user-api.yaml: -------------------------------------------------------------------------------- 1 | Name: user-api 2 | Host: 0.0.0.0 3 | Port: 8888 4 | Auth: 5 | AccessSecret: "12345699999" 6 | AccessExpire: 86400 -------------------------------------------------------------------------------- /micro/rpc/auth/desc/auth.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package authRPC; 4 | 5 | option go_package = "./auth"; 6 | 7 | service AuthService { 8 | 9 | } -------------------------------------------------------------------------------- /server/repositories/modules/auth/auth.go: -------------------------------------------------------------------------------- 1 | package authRepositorysModules 2 | 3 | var AuthRepository = &authRepository{} 4 | 5 | type authRepository struct { 6 | } 7 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/zeromicro/go-zero/zrpc" 4 | 5 | type Config struct { 6 | zrpc.RpcServerConf 7 | } 8 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/zeromicro/go-zero/zrpc" 4 | 5 | type Config struct { 6 | zrpc.RpcServerConf 7 | } 8 | -------------------------------------------------------------------------------- /micro/rpc/captcha/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/zeromicro/go-zero/zrpc" 4 | 5 | type Config struct { 6 | zrpc.RpcServerConf 7 | } 8 | -------------------------------------------------------------------------------- /web/src/components/Icon/dark.tsx: -------------------------------------------------------------------------------- 1 | import moon from '@/assets/svg/moon.svg'; 2 | import { Image } from 'antd'; 3 | 4 | export const DarkSvg = () => { 5 | return ; 6 | }; 7 | -------------------------------------------------------------------------------- /web/src/components/Icon/light.tsx: -------------------------------------------------------------------------------- 1 | import sun from '@/assets/svg/sun.svg'; 2 | import { Image } from 'antd'; 3 | 4 | export const LightSvg = () => { 5 | return ; 6 | }; 7 | -------------------------------------------------------------------------------- /server/utils/modules/contain/contain.go: -------------------------------------------------------------------------------- 1 | package contain 2 | 3 | func StringInSlice(str string, slice []string) bool { 4 | for _, s := range slice { 5 | if s == str { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /micro/api/user/etc/userservice.yaml: -------------------------------------------------------------------------------- 1 | Name: user 2 | Host: 0.0.0.0 3 | Port: 8889 4 | UserService: 5 | Etcd: 6 | Hosts: 7 | - etcd:2379 8 | Key: user.rpc 9 | Auth: 10 | AccessSecret: "12345699999" 11 | AccessExpire: 86400 -------------------------------------------------------------------------------- /web/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "printWidth": 160, 6 | "bracketSameLine": true, 7 | "arrowParens": "always", 8 | "jsxSingleQuote": true, 9 | "bracketSpacing": true 10 | } 11 | -------------------------------------------------------------------------------- /micro/api/test/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/zeromicro/go-zero/rest" 4 | 5 | type Config struct { 6 | rest.RestConf 7 | Auth struct { 8 | AccessSecret string 9 | AccessExpire int64 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | darkMode: "selector", 4 | content: ["./index.html", "src/**/*.{js,ts,jsx,tsx}"], 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | }; 10 | -------------------------------------------------------------------------------- /web/src/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api/login/index'; 2 | export * from './api/pages/index'; 3 | export * from './api/system/index'; 4 | export * from './api/users/index'; 5 | export * from './api/roles/index'; 6 | export * from './api/department/index'; 7 | -------------------------------------------------------------------------------- /micro/api/auth/etc/auth.yaml: -------------------------------------------------------------------------------- 1 | Name: auth 2 | Host: 0.0.0.0 3 | Port: 8888 4 | CaptchaService: 5 | Etcd: 6 | Hosts: 7 | - etcd:2379 8 | Key: captcha.rpc 9 | UserService: 10 | Etcd: 11 | Hosts: 12 | - etcd:2379 13 | Key: user.rpc 14 | -------------------------------------------------------------------------------- /server/dto/modules/common/common.go: -------------------------------------------------------------------------------- 1 | package commonResponsiesModules 2 | 3 | type ExportExcelResponse struct { 4 | IDs []string `json:"ids" form:"ids" binding:"required"` 5 | } 6 | 7 | type HasTotalResponseData struct { 8 | Total int `json:"total"` 9 | List any `json:"list"` 10 | } 11 | -------------------------------------------------------------------------------- /micro/api/auth/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/rest" 5 | "github.com/zeromicro/go-zero/zrpc" 6 | ) 7 | 8 | type Config struct { 9 | rest.RestConf 10 | CaptchaService zrpc.RpcClientConf 11 | UserService zrpc.RpcClientConf 12 | } 13 | -------------------------------------------------------------------------------- /server/dto/modules/commits/commits.go: -------------------------------------------------------------------------------- 1 | package commitsResponsiesModules 2 | 3 | type CommitResponse struct { 4 | CommitID string `json:"commitID"` 5 | Email string `json:"email"` 6 | Author string `json:"author"` 7 | Message string `json:"message"` 8 | Date string `json:"date"` 9 | } 10 | -------------------------------------------------------------------------------- /web/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /micro/api/test/internal/svc/servicecontext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "micro/api/test/internal/config" 5 | ) 6 | 7 | type ServiceContext struct { 8 | Config config.Config 9 | } 10 | 11 | func NewServiceContext(c config.Config) *ServiceContext { 12 | return &ServiceContext{ 13 | Config: c, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Go-React-Admin 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /web/src/components/Icon/translateDark.tsx: -------------------------------------------------------------------------------- 1 | import { Image } from 'antd'; 2 | import EnZh from '@/assets/svg/en-zh.svg'; 3 | import { memo } from 'react'; 4 | 5 | const TranslateDark = () => { 6 | return ; 7 | }; 8 | 9 | export default memo(TranslateDark); 10 | -------------------------------------------------------------------------------- /web/src/components/Icon/translateLight.tsx: -------------------------------------------------------------------------------- 1 | import { Image } from 'antd'; 2 | import ZhEn from '@/assets/svg/zh-en.svg'; 3 | import { memo } from 'react'; 4 | 5 | const TranslateLight = () => { 6 | return ; 7 | }; 8 | 9 | export default memo(TranslateLight); 10 | -------------------------------------------------------------------------------- /server/routers/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package logsRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseLogRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/log") 10 | { 11 | group.GET("/system", controllers.LogsController.GetLogRecords) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /micro/api/user/internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/rest" 5 | "github.com/zeromicro/go-zero/zrpc" 6 | ) 7 | 8 | type Config struct { 9 | rest.RestConf 10 | Auth struct { 11 | AccessSecret string 12 | AccessExpire int64 13 | } 14 | UserService zrpc.RpcClientConf 15 | } 16 | -------------------------------------------------------------------------------- /server/routers/modules/system/system.go: -------------------------------------------------------------------------------- 1 | package systemRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseSystemRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/system") 10 | { 11 | group.GET("", controllers.SystemController.GetSystemInfo) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | .idea 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | pnpm-debug.log* 9 | lerna-debug.log* 10 | 11 | node_modules 12 | dist 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | .vscode/* 18 | !.vscode/extensions.json 19 | .idea 20 | .DS_Store 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /web/src/types/menus/index.ts: -------------------------------------------------------------------------------- 1 | export interface menuType { 2 | pageID: string; 3 | pageName: string; 4 | pagePath: string; 5 | pageIcon: string; 6 | pageComponent: string; 7 | parentPage: string; 8 | children: menuType[]; 9 | pageOrder: number; 10 | canEdit: 1 | 0; 11 | isOutSite: boolean; 12 | outSiteLink: string; 13 | createdAt: string; 14 | updateTime: string; 15 | } 16 | -------------------------------------------------------------------------------- /micro/model/role/role.go: -------------------------------------------------------------------------------- 1 | package roleModlel 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type Role struct { 9 | ID string `gorm:"<-:create;primaryKey"` 10 | RoleName string `gorm:"not null"` 11 | Description string 12 | CanEdit bool `gorm:"default:true"` 13 | CreatedAt time.Time 14 | UpdatedAt time.Time 15 | DeletedAt gorm.DeletedAt `gorm:"index"` 16 | } 17 | -------------------------------------------------------------------------------- /micro/sql/init-master.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Create replication user 5 | mysql -u root -p${MYSQL_ROOT_PASSWORD} < = ({ name, props }) => { 6 | const Comp = AllIcons[name] as any; 7 | return Comp ? : name; 8 | }; 9 | 10 | export default Icon; 11 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/svc/servicecontext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "micro/rpc/role/internal/config" 6 | gormDB "micro/shared/gorm" 7 | ) 8 | 9 | type ServiceContext struct { 10 | Config config.Config 11 | GormDB *gorm.DB 12 | } 13 | 14 | func NewServiceContext(c config.Config) *ServiceContext { 15 | return &ServiceContext{ 16 | Config: c, 17 | GormDB: gormDB.NewGorm(), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/svc/servicecontext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "micro/rpc/user/internal/config" 6 | gormDB "micro/shared/gorm" 7 | ) 8 | 9 | type ServiceContext struct { 10 | Config config.Config 11 | GormDB *gorm.DB 12 | } 13 | 14 | func NewServiceContext(c config.Config) *ServiceContext { 15 | return &ServiceContext{ 16 | Config: c, 17 | GormDB: gormDB.NewGorm(), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/routers/modules/commits/commits.go: -------------------------------------------------------------------------------- 1 | package commitsRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseCommitsRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/log") 10 | { 11 | group.GET("/commits", controllers.CommitsController.GetCommits) 12 | group.GET("/commits/count", controllers.CommitsController.GetCommitsCount) 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/routers/modules/template/template.go: -------------------------------------------------------------------------------- 1 | package templateRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseTTemplateRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/template") 10 | { 11 | group.POST("", controllers.TemplateController.CreateTemplate) 12 | group.POST("/download", controllers.TemplateController.DownloadTemplateZip) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "github.com/Xi-Yuer/cms/bootStrap" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | bootStrap.Start() 14 | quit := make(chan os.Signal, 1) 15 | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 16 | <-quit 17 | _, cancel := context.WithTimeout(context.Background(), 5*time.Second) 18 | defer cancel() 19 | } 20 | -------------------------------------------------------------------------------- /server/services/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package logsServiceModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/dto" 5 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 6 | ) 7 | 8 | var LogsService = &logsService{} 9 | 10 | type logsService struct{} 11 | 12 | func (receiver *logsService) GetLogRecords(params *dto.Page) (*dto.HasTotalResponseData, error) { 13 | return repositories.LogsRepository.GetLogRecords(params) 14 | } 15 | -------------------------------------------------------------------------------- /server/routers/modules/auth/auth.go: -------------------------------------------------------------------------------- 1 | package authRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseAuthRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/auth") 10 | { 11 | group.POST("/login", controllers.AuthController.Login) 12 | group.GET("/captcha", controllers.AuthController.Captcha) 13 | group.GET("/cookie", controllers.AuthController.Cookie) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/utils/modules/unique/unique.go: -------------------------------------------------------------------------------- 1 | package unique 2 | 3 | // RemoveDuplicatesAndEmpty 移除数组中重复元素 4 | func RemoveDuplicatesAndEmpty(a []string) (ret []string) { 5 | ret = make([]string, 0) 6 | for i := 0; i < len(a); i++ { 7 | repeat := false 8 | for j := i + 1; j < len(a); j++ { 9 | if a[i] == a[j] { 10 | repeat = true 11 | break 12 | } 13 | } 14 | if !repeat { 15 | ret = append(ret, a[i]) 16 | } 17 | } 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /micro/api/test/internal/types/types.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | package types 3 | 4 | type LoginReq struct { 5 | Username string `json:"username"` 6 | Password string `json:"password"` 7 | } 8 | 9 | type LoginResp struct { 10 | ID string `json:"id"` 11 | Name string `json:"name"` 12 | } 13 | 14 | type UserInfoReq struct { 15 | ID string `json:"id"` 16 | } 17 | 18 | type UserInfoResp struct { 19 | Name string `json:"name"` 20 | } 21 | -------------------------------------------------------------------------------- /web/src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useAppDispatch, useAppSelector } from '@/store'; 2 | import { changeThemeMode } from '@/store/UIStore'; 3 | 4 | export const useTheme = () => { 5 | const themeMode = useAppSelector((state) => state.UIStore.themeMode); 6 | const dispatch = useAppDispatch(); 7 | const toggleTheme = () => { 8 | dispatch(changeThemeMode(themeMode === 'dark' ? 'light' : 'dark')); 9 | }; 10 | 11 | return { 12 | themeMode, 13 | toggleTheme, 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /micro/api/auth/internal/types/types.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | package types 3 | 4 | type LoginRequest struct { 5 | Account string `form:"account"` 6 | Password string `form:"password"` 7 | Captcha string `form:"captcha"` 8 | SessionID string `form:"sessionId"` 9 | } 10 | 11 | type CommonResponse struct { 12 | Code int64 `json:"code"` 13 | Data interface{} `json:"data"` 14 | Msg string `json:"msg"` 15 | } 16 | 17 | type EmptyRequest struct { 18 | } 19 | -------------------------------------------------------------------------------- /web/src/components/Iframe/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from 'react'; 2 | 3 | export interface IProps { 4 | src: string; 5 | } 6 | 7 | const Iframe: FC = ({ src }) => { 8 | return ( 9 | 15 | ); 16 | }; 17 | 18 | export default memo(Iframe); 19 | -------------------------------------------------------------------------------- /micro/rpc/captcha/internal/svc/servicecontext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "github.com/redis/go-redis/v9" 5 | appConfig "micro/config" 6 | "micro/rpc/captcha/internal/config" 7 | redisDB "micro/shared/redis" 8 | ) 9 | 10 | type ServiceContext struct { 11 | Config config.Config 12 | Redis *redis.Client 13 | } 14 | 15 | func NewServiceContext(c config.Config) *ServiceContext { 16 | 17 | return &ServiceContext{ 18 | Config: c, 19 | Redis: redisDB.InitRedis(appConfig.RedisHost), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /micro/api/auth/Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用官方的 Go 语言镜像作为基础镜像 2 | FROM golang:1.21.1-alpine 3 | 4 | # 设置 Go 模块代理 5 | ENV GOPROXY=https://goproxy.cn,direct 6 | 7 | # 设置工作目录 8 | WORKDIR /app 9 | 10 | COPY . . 11 | 12 | # 创建一个目录用于存放编译后的二进制文件和配置文件 13 | RUN mkdir -p ./bin 14 | 15 | # 安装依赖 16 | RUN go mod download 17 | 18 | # 编译 Go 程序 19 | RUN go build -o ./bin/cmd ./api/auth/auth.go 20 | 21 | COPY ./api/auth/etc ./bin/etc 22 | 23 | # 暴露服务的端口 24 | EXPOSE 8888 25 | 26 | # 定义容器启动时运行的命令 27 | CMD ["./bin/cmd", "-f", "./bin/etc/auth.yaml"] 28 | -------------------------------------------------------------------------------- /micro/rpc/role/Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用官方的 Go 语言镜像作为基础镜像 2 | FROM golang:1.21.1-alpine 3 | 4 | # 设置 Go 模块代理 5 | ENV GOPROXY=https://goproxy.cn,direct 6 | 7 | # 设置工作目录 8 | WORKDIR /app 9 | 10 | COPY . . 11 | 12 | # 创建一个目录用于存放编译后的二进制文件和配置文件 13 | RUN mkdir -p ./bin 14 | 15 | # 安装依赖 16 | RUN go mod download 17 | 18 | # 编译 Go 程序 19 | RUN go build -o ./bin/cmd ./rpc/role/role.go 20 | 21 | COPY ./rpc/role/etc ./bin/etc 22 | 23 | # 暴露服务的端口 24 | EXPOSE 8888 25 | 26 | # 定义容器启动时运行的命令 27 | CMD ["./bin/cmd", "-f", "./bin/etc/role.yaml"] 28 | -------------------------------------------------------------------------------- /micro/rpc/user/Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用官方的 Go 语言镜像作为基础镜像 2 | FROM golang:1.21.1-alpine 3 | 4 | # 设置 Go 模块代理 5 | ENV GOPROXY=https://goproxy.cn,direct 6 | 7 | # 设置工作目录 8 | WORKDIR /app 9 | 10 | COPY . . 11 | 12 | # 创建一个目录用于存放编译后的二进制文件和配置文件 13 | RUN mkdir -p ./bin 14 | 15 | # 安装依赖 16 | RUN go mod download 17 | 18 | # 编译 Go 程序 19 | RUN go build -o ./bin/cmd ./rpc/user/user.go 20 | 21 | COPY ./rpc/user/etc ./bin/etc 22 | 23 | # 暴露服务的端口 24 | EXPOSE 8888 25 | 26 | # 定义容器启动时运行的命令 27 | CMD ["./bin/cmd", "-f", "./bin/etc/user.yaml"] 28 | -------------------------------------------------------------------------------- /micro/api/user/internal/svc/servicecontext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/zrpc" 5 | "micro/api/user/internal/config" 6 | "micro/rpc/user/userservice" 7 | ) 8 | 9 | type ServiceContext struct { 10 | Config config.Config 11 | UserService userservice.UserService 12 | } 13 | 14 | func NewServiceContext(c config.Config) *ServiceContext { 15 | return &ServiceContext{ 16 | Config: c, 17 | UserService: userservice.NewUserService(zrpc.MustNewClient(c.UserService)), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | interface ImportMetaEnv { 3 | readonly VITE_APP_BASE_URL: string; 4 | } 5 | 6 | interface ImportMeta { 7 | readonly env: ImportMetaEnv; 8 | } 9 | 10 | import 'i18next'; 11 | import ns1 from 'locales/en/ns1.json'; 12 | import ns2 from 'locales/en/ns2.json'; 13 | 14 | declare module 'i18next' { 15 | interface CustomTypeOptions { 16 | defaultNS: 'ns1'; 17 | resources: { 18 | ns1: typeof ns1; 19 | ns2: typeof ns2; 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/src/utils/cache/index.ts: -------------------------------------------------------------------------------- 1 | class Cache { 2 | set(key: string, value: any) { 3 | window.localStorage.setItem(key, JSON.stringify(value)); 4 | } 5 | 6 | get(key: string) { 7 | const result = window.localStorage.getItem(key); 8 | if (result) { 9 | return JSON.parse(result); 10 | } 11 | } 12 | 13 | remove(key: string) { 14 | return window.localStorage.removeItem(key); 15 | } 16 | 17 | clear() { 18 | window.localStorage.clear(); 19 | } 20 | } 21 | 22 | export const cache = new Cache(); 23 | -------------------------------------------------------------------------------- /micro/api/user/Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用官方的 Go 语言镜像作为基础镜像 2 | FROM golang:1.21.1-alpine 3 | 4 | # 设置 Go 模块代理 5 | ENV GOPROXY=https://goproxy.cn,direct 6 | 7 | # 设置工作目录 8 | WORKDIR /app 9 | 10 | COPY . . 11 | 12 | # 创建一个目录用于存放编译后的二进制文件和配置文件 13 | RUN mkdir -p ./bin 14 | 15 | # 安装依赖 16 | RUN go mod download 17 | 18 | # 编译 Go 程序 19 | RUN go build -o ./bin/cmd ./api/user/userservice.go 20 | 21 | COPY ./api/user/etc ./bin/etc 22 | 23 | # 暴露服务的端口 24 | EXPOSE 8888 25 | 26 | # 定义容器启动时运行的命令 27 | CMD ["./bin/cmd", "-f", "./bin/etc/userservice.yaml"] 28 | -------------------------------------------------------------------------------- /micro/rpc/captcha/Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用官方的 Go 语言镜像作为基础镜像 2 | FROM golang:1.21.1-alpine 3 | 4 | # 设置 Go 模块代理 5 | ENV GOPROXY=https://goproxy.cn,direct 6 | 7 | # 设置工作目录 8 | WORKDIR /app 9 | 10 | COPY . . 11 | 12 | # 创建一个目录用于存放编译后的二进制文件和配置文件 13 | RUN mkdir -p ./bin 14 | 15 | # 安装依赖 16 | RUN go mod download 17 | 18 | # 编译 Go 程序 19 | RUN go build -o ./bin/cmd ./rpc/captcha/captcha.go 20 | 21 | COPY ./rpc/captcha/etc ./bin/etc 22 | 23 | # 暴露服务的端口 24 | EXPOSE 8888 25 | 26 | # 定义容器启动时运行的命令 27 | CMD ["./bin/cmd", "-f", "./bin/etc/captcha.yaml"] 28 | -------------------------------------------------------------------------------- /server/template/server/route/route.go: -------------------------------------------------------------------------------- 1 | package TableNameRoute 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/template/server/controller" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseTableNameRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/TableName") 10 | c := TableNameController.NewTableNameController() 11 | { 12 | group.GET("", c.GetTableNameListRepo) 13 | group.POST("", c.CreateTableNameRecordRepo) 14 | group.PUT("/:id", c.UpdateTableNameRecordRepo) 15 | group.DELETE("/:id", c.DeleteTableNameRecordRepo) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/template/server/dto/dto.go: -------------------------------------------------------------------------------- 1 | package TableNameDto 2 | 3 | type TableNameCreateRequestDTO struct { 4 | Name string `json:"name" binding:"required"` 5 | Age int8 `json:"age" binding:"required"` 6 | } 7 | 8 | type TableNameUpdateRequestDTO struct { 9 | Name string `json:"name" binding:"required"` 10 | Age int8 `json:"age" binding:"required"` 11 | } 12 | 13 | type TableNameFindRequestDTO struct { 14 | Name string `json:"name"` 15 | Age int8 `json:"age"` 16 | Limit int64 `json:"limit"` 17 | Offset int64 `json:"offset"` 18 | } 19 | -------------------------------------------------------------------------------- /micro/model/user/user.go: -------------------------------------------------------------------------------- 1 | package userModel 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type User struct { 9 | ID string `gorm:"<-:create;primaryKey"` 10 | Account string `gorm:"<-:create;unique"` 11 | Password string `gorm:"not null"` 12 | NickName string 13 | Avatar string 14 | Status bool `gorm:"default:true"` 15 | DepartmentID string 16 | IsAdmin bool `gorm:"default:false"` 17 | CreatedAt time.Time 18 | UpdatedAt time.Time 19 | DeletedAt gorm.DeletedAt `gorm:"index"` 20 | } 21 | -------------------------------------------------------------------------------- /server/routers/modules/department/department.go: -------------------------------------------------------------------------------- 1 | package departmentRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseDepartmentRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/department") 10 | group.POST("", controllers.DepartmentController.CreateDepartment) 11 | group.DELETE("/:id", controllers.DepartmentController.DeleteDepartment) 12 | group.GET("", controllers.DepartmentController.GetDepartments) 13 | group.PATCH("/:id", controllers.DepartmentController.UpdateDepartment) 14 | } 15 | -------------------------------------------------------------------------------- /server/routers/modules/swagger/swagger.go: -------------------------------------------------------------------------------- 1 | package swaggerRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/config" 5 | "github.com/Xi-Yuer/cms/docs" 6 | "github.com/gin-gonic/gin" 7 | swaggerfiles "github.com/swaggo/files" 8 | ginSwagger "github.com/swaggo/gin-swagger" 9 | ) 10 | 11 | func UseSwaggerRoutes(r *gin.Engine) { 12 | docs.SwaggerInfo.Title = "后台管理系统API接口" 13 | docs.SwaggerInfo.Description = "后台管理系统API接口" 14 | docs.SwaggerInfo.BasePath = config.Config.BASEURL 15 | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) 16 | } 17 | -------------------------------------------------------------------------------- /server/utils/modules/bcrypt/bcrypt.go: -------------------------------------------------------------------------------- 1 | package bcrypt 2 | 3 | import "golang.org/x/crypto/bcrypt" 4 | 5 | type Bcrypt struct { 6 | } 7 | 8 | func (b *Bcrypt) HashPassword(password string) (string, error) { 9 | generateFromPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) 10 | if err != nil { 11 | return "", err 12 | } 13 | return string(generateFromPassword), nil 14 | } 15 | 16 | func (b *Bcrypt) VerifyPassword(password string, hash string) error { 17 | return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) 18 | } 19 | -------------------------------------------------------------------------------- /server/routers/modules/timeTask/timeTask.go: -------------------------------------------------------------------------------- 1 | package timeTaskRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseTimeTaskRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/timeTask") 10 | 11 | { 12 | group.POST("/start/:id", controllers.TimeTaskController.StartTimeTask) 13 | group.POST("/stop/:id", controllers.TimeTaskController.StopTimeTask) 14 | group.PATCH("/update/:id", controllers.TimeTaskController.UpdateTimeTask) 15 | group.GET("", controllers.TimeTaskController.GetTimeTask) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/template/server/dto/dto.tmpl: -------------------------------------------------------------------------------- 1 | package {{.TableName}}Dto 2 | 3 | type {{.TableName}}CreateRequestDTO struct { 4 | {{range .Fields}} {{.Name}} {{.Type}} `json:"{{.Name}}" binding:"required"` 5 | {{end}} 6 | } 7 | 8 | type {{.TableName}}UpdateRequestDTO struct { 9 | {{range .Fields}} {{.Name}} {{.Type}} `json:"{{.Name}}" binding:"required"` 10 | {{end}} 11 | } 12 | 13 | type {{.TableName}}FindRequestDTO struct { 14 | {{range .Fields}} {{.Name}} {{.Type}} `json:"{{.Name}}" binding:"required"` 15 | {{end}} Limit int64 `json:"limit"` 16 | Offset int64 `json:"offset"` 17 | } 18 | -------------------------------------------------------------------------------- /web/src/components/Theme/index.tsx: -------------------------------------------------------------------------------- 1 | import { LightSvg } from '@/components/Icon/light.tsx'; 2 | import { DarkSvg } from '@/components/Icon/dark.tsx'; 3 | import { Switch } from 'antd'; 4 | import { useTheme } from '@/hooks/useTheme.ts'; 5 | import { memo } from 'react'; 6 | 7 | const ThemeBar = () => { 8 | const { toggleTheme, themeMode } = useTheme(); 9 | return ( 10 | <> 11 | } unCheckedChildren={} onChange={toggleTheme} checked={themeMode === 'dark'} /> 12 | 13 | ); 14 | }; 15 | 16 | export default memo(ThemeBar); 17 | -------------------------------------------------------------------------------- /web/src/router/index.tsx: -------------------------------------------------------------------------------- 1 | import { RouteObject } from 'react-router-dom'; 2 | import { constants } from '@/constant'; 3 | 4 | import Login from '@/pages/Login'; 5 | import Main from '@/LayOut'; 6 | import NotFond from '@/pages/NotFond'; 7 | 8 | export const routes: RouteObject[] = [ 9 | { 10 | path: constants.routePath.login, 11 | element: , 12 | }, 13 | { 14 | path: constants.routePath.main, 15 | element:
, 16 | children: [], 17 | }, 18 | { 19 | path: '*', 20 | element: , 21 | }, 22 | ]; 23 | 24 | export default routes; 25 | -------------------------------------------------------------------------------- /web/src/pages/NotFond/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from 'react'; 2 | import { Image } from 'antd'; 3 | import NotFondImage from '@/assets/image/404.png'; 4 | 5 | const NotFond: FC = () => { 6 | return ( 7 |
8 | 9 |
10 | OOPS! 11 | Page Not Font! 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default memo(NotFond); 18 | -------------------------------------------------------------------------------- /server/template/server/route/route.tmpl: -------------------------------------------------------------------------------- 1 | package {{ .TableName }}Route 2 | 3 | import ( 4 | {{ .TableName }}Controller "{{ .Package }}/template/controller" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func Use{{ .TableName }}Routes(r *gin.RouterGroup) { 9 | group := r.Group("/{{ .TableName }}") 10 | c := {{ .TableName }}Controller.New{{ .TableName }}Controller() 11 | { 12 | group.GET("", c.Get{{ .TableName }}ListRepo) 13 | group.POST("", c.Create{{ .TableName }}RecordRepo) 14 | group.PUT("/:id", c.Update{{ .TableName }}RecordRepo) 15 | group.DELETE("/:id", c.Delete{{ .TableName }}RecordRepo) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /web/src/components/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo, ReactNode } from 'react'; 2 | import classNames from 'classnames'; 3 | import { useAppSelector } from '@/store'; 4 | 5 | const Card: FC<{ children: ReactNode }> = ({ children }) => { 6 | const { themeMode } = useAppSelector((state) => state.UIStore); 7 | return ( 8 |
12 | {children} 13 |
14 | ); 15 | }; 16 | 17 | export default memo(Card); 18 | -------------------------------------------------------------------------------- /micro/api/test/desc/test.api: -------------------------------------------------------------------------------- 1 | syntax = "v1" 2 | 3 | type LoginReq { 4 | Username string `json:"username"` 5 | Password string `json:"password"` 6 | } 7 | 8 | type LoginResp { 9 | ID string `json:"id"` 10 | Name string `json:"name"` 11 | } 12 | 13 | type UserInfoReq { 14 | ID string `json:"id"` 15 | } 16 | 17 | type UserInfoResp { 18 | Name string `json:"name"` 19 | } 20 | 21 | service user-api { 22 | @handler login 23 | post /user/login (LoginReq) returns (LoginResp) 24 | } 25 | 26 | @server ( 27 | jwt: Auth // 开启 jwt 认证 28 | ) 29 | service user-api { 30 | @handler userInfo 31 | post /user/info (UserInfoReq) returns (UserInfoResp) 32 | } -------------------------------------------------------------------------------- /web/src/components/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from 'react'; 2 | 3 | const Footer: FC = () => { 4 | return ( 5 | <> 6 |
7 | © Design Xi-Yuer 8 | 9 | 10 | 备案号: 蜀ICP备2022015920号-2 11 | 12 | 13 |
14 | 15 | ); 16 | }; 17 | 18 | export default memo(Footer); 19 | -------------------------------------------------------------------------------- /server/routers/modules/pages/pages.go: -------------------------------------------------------------------------------- 1 | package pagesRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UsePagesRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/pages") 10 | { 11 | group.POST("", controllers.PagesController.CreatePage) 12 | group.DELETE("/:id", controllers.PagesController.DeletePage) 13 | group.GET("", controllers.PagesController.GetPages) 14 | group.GET("/user", controllers.PagesController.GetUserMenus) 15 | group.GET("/role/:id", controllers.PagesController.GetPagesByRoleID) 16 | group.PATCH("/:id", controllers.PagesController.UpdatePage) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /web/src/service/api/logs/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { IHasTotalResponse, Page } from '@/service'; 3 | import { AxiosResponse } from 'axios'; 4 | 5 | export interface ISysTemLogsResponse { 6 | id: string; 7 | requestDuration: string; 8 | requestMethod: string; 9 | requestPath: string; 10 | requestStatus: string; 11 | userID: string; 12 | userIP: string; 13 | userName: string; 14 | createTime: string; 15 | } 16 | 17 | export const getSysTemLogsRequest = (params: Page) => { 18 | return request.get>>({ 19 | url: '/log/system', 20 | params, 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /micro/api/auth/internal/handler/routes.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | package handler 3 | 4 | import ( 5 | "net/http" 6 | 7 | "micro/api/auth/internal/svc" 8 | 9 | "github.com/zeromicro/go-zero/rest" 10 | ) 11 | 12 | func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { 13 | server.AddRoutes( 14 | []rest.Route{ 15 | { 16 | Method: http.MethodPost, 17 | Path: "/auth/login", 18 | Handler: LoginHandler(serverCtx), 19 | }, 20 | { 21 | Method: http.MethodGet, 22 | Path: "/auth/captcha", 23 | Handler: CaptchaHandler(serverCtx), 24 | }, 25 | }, 26 | rest.WithPrefix("/v1"), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /web/src/components/Auth/index.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren, ReactNode } from 'react'; 2 | import { useAppSelector } from '@/store'; 3 | 4 | interface Props { 5 | permission: string | string[]; 6 | } 7 | 8 | const Auth: (props: PropsWithChildren) => ReactNode = ({ children, permission }) => { 9 | const allInterfaceDic = useAppSelector((state) => state.UserStore.userInterfaceDic); 10 | if (Array.isArray(permission)) { 11 | return permission.some((item) => allInterfaceDic.includes(item)) ? children : null; 12 | } 13 | if (allInterfaceDic.includes(permission)) { 14 | return children; 15 | } 16 | return null; 17 | }; 18 | 19 | export default Auth; 20 | -------------------------------------------------------------------------------- /web/src/assets/svg/moon.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /micro/shared/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redisDB 2 | 3 | import ( 4 | "context" 5 | "github.com/redis/go-redis/v9" 6 | "log" 7 | "sync" 8 | ) 9 | 10 | var ( 11 | redisClient *redis.Client 12 | once sync.Once 13 | ) 14 | 15 | // InitRedis 初始化 Redis 客户端 16 | func InitRedis(redisAddr string) *redis.Client { 17 | once.Do(func() { 18 | // 创建 Redis 客户端 19 | redisClient = redis.NewClient(&redis.Options{ 20 | Addr: redisAddr, 21 | // 设置连接池大小 22 | PoolSize: 10, 23 | }) 24 | 25 | // 确保连接到 Redis 26 | _, err := redisClient.Ping(context.Background()).Result() 27 | if err != nil { 28 | log.Fatalf("无法连接到 Redis: %v", err) 29 | } 30 | }) 31 | 32 | return redisClient 33 | } 34 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 基于 Node.js 镜像创建一个 Docker 镜像 2 | FROM node:18 3 | 4 | # 进入前端目录 5 | RUN cd /web 6 | 7 | # 将当前工作目录设置为/app 8 | WORKDIR /app 9 | 10 | # 将 package.json 和 package-lock.json 复制到 /app 目录下 11 | COPY package*.json ./ 12 | 13 | # 运行 npm install 安装依赖 14 | RUN npm install 15 | 16 | # 将源代码复制到 /app 目录下 17 | COPY . . 18 | 19 | # 打包构建 20 | RUN npm run build 21 | 22 | # 将构建后的代码复制到 nginx 镜像中 23 | FROM nginx:latest 24 | COPY --from=0 /app/dist /usr/share/nginx/html 25 | 26 | # 暴露容器的 8080 端口,此处其实只是一个声明作用,不写的话也可以,后面运行容器的 27 | # docker run --name container_name -p :命令中container_port可以覆盖此处的声明,不写就默认80端口 28 | EXPOSE 8080 29 | 30 | # 启动 nginx 服务 31 | CMD ["nginx", "-g", "daemon off;"] 32 | -------------------------------------------------------------------------------- /web/src/local/index.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next'; 2 | import { initReactI18next } from 'react-i18next'; 3 | import LanguageDetector from 'i18next-browser-languagedetector'; 4 | import { constants } from '@/constant'; 5 | import { cache } from '@/utils'; 6 | import en from './en'; 7 | import zh from './zh'; 8 | 9 | const i18next = i18n 10 | .use(LanguageDetector) 11 | .use(initReactI18next) 12 | .init({ 13 | debug: false, 14 | fallbackLng: cache.get(constants.localStorage.lang) === 'enUS' ? 'en' : 'zh' || 'zh', 15 | interpolation: { 16 | escapeValue: false, 17 | }, 18 | resources: { 19 | en, 20 | zh, 21 | }, 22 | }); 23 | 24 | export default i18next; 25 | -------------------------------------------------------------------------------- /server/utils/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package logsModules 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | ) 8 | 9 | var Log = &log{} 10 | 11 | type log struct { 12 | } 13 | 14 | func baseInfo() string { 15 | t := time.Now().Format("2006-01-02 15:04:05") 16 | return fmt.Sprintf("[%s]", t) 17 | } 18 | 19 | func (l *log) Info(v ...interface{}) { 20 | fmt.Printf("【信息】%s %s\n", baseInfo(), v) 21 | } 22 | 23 | func (l *log) Warn(v ...interface{}) { 24 | fmt.Printf("【警告】%s %s\n", baseInfo(), v) 25 | } 26 | func (l *log) Error(v ...interface{}) { 27 | fmt.Printf("【错误】%s %s\n", baseInfo(), v) 28 | } 29 | func (l *log) Panic(v ...interface{}) { 30 | fmt.Printf("【系统错误】%s %s\n", baseInfo(), v) 31 | os.Exit(0) 32 | } 33 | -------------------------------------------------------------------------------- /web/src/assets/svg/start.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/assets/svg/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /micro/api/auth/internal/svc/servicecontext.go: -------------------------------------------------------------------------------- 1 | package svc 2 | 3 | import ( 4 | "github.com/zeromicro/go-zero/zrpc" 5 | "micro/api/auth/internal/config" 6 | "micro/rpc/captcha/captchaservice" 7 | "micro/rpc/user/userservice" 8 | ) 9 | 10 | type ServiceContext struct { 11 | Config config.Config 12 | CaptchaService captchaservice.CaptchaService 13 | UserService userservice.UserService 14 | } 15 | 16 | func NewServiceContext(c config.Config) *ServiceContext { 17 | return &ServiceContext{ 18 | Config: c, 19 | CaptchaService: captchaservice.NewCaptchaService(zrpc.MustNewClient(c.CaptchaService)), 20 | UserService: userservice.NewUserService(zrpc.MustNewClient(c.UserService)), 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/src/utils/echarts/index.ts: -------------------------------------------------------------------------------- 1 | // import the core library. 2 | // Import the echarts core module, which provides the necessary interfaces for using echarts. 3 | import * as echarts from 'echarts/core'; 4 | // Import charts, all with Chart suffix 5 | import { BarChart } from 'echarts/charts'; 6 | // import components, all suffixed with Component 7 | import { GridComponent, TitleComponent, TooltipComponent } from 'echarts/components'; 8 | // Import renderer, note that introducing the CanvasRenderer or SVGRenderer is a required step 9 | import { CanvasRenderer } from 'echarts/renderers'; 10 | 11 | // Register the required components 12 | echarts.use([TitleComponent, TooltipComponent, GridComponent, BarChart, CanvasRenderer]); 13 | -------------------------------------------------------------------------------- /micro/rpc/captcha/desc/captcha.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package captchaRPC; 4 | 5 | option go_package = "./captcha"; 6 | 7 | service CaptchaService { 8 | rpc GenerateCaptcha(GenerateCaptchaRequest) returns (GenerateCaptchaResponse); 9 | rpc VerifyCaptcha(VerifyCaptchaRequest) returns (VerifyCaptchaResponse); 10 | } 11 | 12 | message GenerateCaptchaRequest { 13 | string session_id = 1; 14 | } 15 | 16 | message GenerateCaptchaResponse { 17 | string captcha_code = 1; 18 | int64 expires_at = 2; 19 | string captcha = 3; 20 | } 21 | 22 | message VerifyCaptchaRequest { 23 | string session_id = 1; 24 | string captcha_code = 2; 25 | } 26 | 27 | message VerifyCaptchaResponse { 28 | bool valid = 1; 29 | } 30 | -------------------------------------------------------------------------------- /server/middlewares/modules/session/session.go: -------------------------------------------------------------------------------- 1 | package sessionMiddleWareModule 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/config" 5 | "github.com/gin-contrib/sessions" 6 | "github.com/gin-contrib/sessions/cookie" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func Session(keyPairs string) gin.HandlerFunc { 11 | store := sessionConfig() 12 | return sessions.Sessions(keyPairs, store) 13 | } 14 | func sessionConfig() sessions.Store { 15 | sessionMaxAge := 3600 16 | sessionSecret := config.Config.APP.SESSIONSECRET 17 | var store sessions.Store 18 | store = cookie.NewStore([]byte(sessionSecret)) 19 | store.Options(sessions.Options{ 20 | MaxAge: sessionMaxAge, //seconds 21 | Path: "/", 22 | }) 23 | return store 24 | } 25 | -------------------------------------------------------------------------------- /web/src/utils/format/index.ts: -------------------------------------------------------------------------------- 1 | export function formatFileSize(bytes: number) { 2 | if (bytes === 0) return '0 Bytes'; 3 | 4 | const k = 1024; 5 | const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 6 | const i = Math.floor(Math.log(bytes) / Math.log(k)); 7 | 8 | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; 9 | } 10 | 11 | export function formatTimer(seconds: number) { 12 | // 格式化计时器 13 | const hours = Math.floor(seconds / 3600); 14 | const minutes = Math.floor((seconds % 3600) / 60); 15 | const remainingSeconds = (seconds % 60).toFixed(0); 16 | 17 | return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; 18 | } 19 | -------------------------------------------------------------------------------- /server/routers/modules/roles/roles.go: -------------------------------------------------------------------------------- 1 | package rolesRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseRolesRoutes(r *gin.RouterGroup) { 9 | group := r.Group("/roles") 10 | { 11 | group.POST("/query", controllers.RoleController.GetRoles) 12 | group.POST("", controllers.RoleController.CreateRole) 13 | group.DELETE("/:id", controllers.RoleController.DeleteRole) 14 | group.PATCH("/:id", controllers.RoleController.UpdateRole) 15 | group.POST("/bindUser", controllers.RoleController.CreateOneRecord) 16 | group.POST("/deBindUser", controllers.RoleController.DeleteOneRecord) 17 | group.POST("/export", controllers.RoleController.ExportExcel) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /micro/api/auth/auth.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "micro/api/auth/internal/config" 8 | "micro/api/auth/internal/handler" 9 | "micro/api/auth/internal/svc" 10 | 11 | "github.com/zeromicro/go-zero/core/conf" 12 | "github.com/zeromicro/go-zero/rest" 13 | ) 14 | 15 | var configFile = flag.String("f", "etc/auth.yaml", "the config file") 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | var c config.Config 21 | conf.MustLoad(*configFile, &c) 22 | 23 | server := rest.MustNewServer(c.RestConf) 24 | defer server.Stop() 25 | 26 | ctx := svc.NewServiceContext(c) 27 | handler.RegisterHandlers(server, ctx) 28 | 29 | fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) 30 | server.Start() 31 | } 32 | -------------------------------------------------------------------------------- /server/routers/modules/interface/interface.go: -------------------------------------------------------------------------------- 1 | package interfaceRpoterModuels 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseInterfaceRouter(r *gin.RouterGroup) { 9 | group := r.Group("/interface") 10 | { 11 | 12 | group.POST("", controllers.InterfaceController.CreateInterface) 13 | group.GET("", controllers.InterfaceController.GetAllInterface) 14 | group.DELETE("/:id", controllers.InterfaceController.DeleteInterface) 15 | group.PATCH("/:id", controllers.InterfaceController.UpdateInterface) 16 | group.GET("/page/:id", controllers.InterfaceController.GetInterfaceByPageID) 17 | group.GET("/role/:id", controllers.InterfaceController.GetInterfacesByRoleID) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /micro/api/user/userservice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "micro/api/user/internal/config" 8 | "micro/api/user/internal/handler" 9 | "micro/api/user/internal/svc" 10 | 11 | "github.com/zeromicro/go-zero/core/conf" 12 | "github.com/zeromicro/go-zero/rest" 13 | ) 14 | 15 | var configFile = flag.String("f", "etc/userservice.yaml", "the config file") 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | var c config.Config 21 | conf.MustLoad(*configFile, &c) 22 | 23 | server := rest.MustNewServer(c.RestConf) 24 | defer server.Stop() 25 | 26 | ctx := svc.NewServiceContext(c) 27 | handler.RegisterHandlers(server, ctx) 28 | 29 | fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) 30 | server.Start() 31 | } 32 | -------------------------------------------------------------------------------- /micro/api/test/internal/handler/routes.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | package handler 3 | 4 | import ( 5 | "net/http" 6 | 7 | "micro/api/test/internal/svc" 8 | 9 | "github.com/zeromicro/go-zero/rest" 10 | ) 11 | 12 | func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { 13 | server.AddRoutes( 14 | []rest.Route{ 15 | { 16 | Method: http.MethodPost, 17 | Path: "/user/login", 18 | Handler: loginHandler(serverCtx), 19 | }, 20 | }, 21 | ) 22 | 23 | server.AddRoutes( 24 | []rest.Route{ 25 | { 26 | Method: http.MethodPost, 27 | Path: "/user/info", 28 | Handler: userInfoHandler(serverCtx), 29 | }, 30 | }, 31 | rest.WithJwt(serverCtx.Config.Auth.AccessSecret), 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /server/bootStrap/bootStrap.go: -------------------------------------------------------------------------------- 1 | package bootStrap 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/config" 5 | "github.com/Xi-Yuer/cms/db" 6 | "github.com/Xi-Yuer/cms/routers" 7 | "github.com/Xi-Yuer/cms/utils" 8 | ) 9 | 10 | func Start() { 11 | if err := db.InitDB(); err != nil { 12 | utils.Log.Panic(err) 13 | return 14 | } 15 | 16 | if err := utils.File.CheckOrCreateFolder(config.Config.APP.FILEPATH); err != nil { 17 | utils.Log.Panic(err) 18 | return 19 | } 20 | r := routers.SetUpRouters() 21 | go func() { 22 | err := r.Run(config.Config.APP.PORT) 23 | if err != nil { 24 | utils.Log.Panic(err) 25 | } 26 | }() 27 | 28 | utils.Log.Info("服务器启动成功,运行端口", config.Config.APP.PORT) 29 | utils.Log.Info("接口文档地址", config.Config.APP.SWAGPATH) 30 | } 31 | -------------------------------------------------------------------------------- /server/middlewares/modules/request/log.go: -------------------------------------------------------------------------------- 1 | package requestMiddlewareModule 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Xi-Yuer/cms/utils" 6 | "github.com/gin-gonic/gin" 7 | "time" 8 | ) 9 | 10 | var RequestMiddlewareModule = &requestMiddlewareModule{} 11 | 12 | type requestMiddlewareModule struct{} 13 | 14 | // RequestLoggerMiddleware 请求日志中间件 15 | func (r *requestMiddlewareModule) RequestLoggerMiddleware(context *gin.Context) { 16 | path := context.Request.URL.Path 17 | methods := context.Request.Method 18 | params := context.Request.URL.RawQuery 19 | status := context.Writer.Status() 20 | start := time.Now() 21 | context.Next() 22 | duration := time.Since(start) 23 | utils.Log.Info(fmt.Sprintf("%v %s %s %s %s", status, methods, duration, path, params)) 24 | } 25 | -------------------------------------------------------------------------------- /micro/api/test/internal/handler/loginhandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/test/internal/logic" 8 | "micro/api/test/internal/svc" 9 | "micro/api/test/internal/types" 10 | ) 11 | 12 | func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.LoginReq 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewLoginLogic(r.Context(), svcCtx) 21 | resp, err := l.Login(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/api/auth/internal/handler/loginhandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/auth/internal/logic" 8 | "micro/api/auth/internal/svc" 9 | "micro/api/auth/internal/types" 10 | ) 11 | 12 | func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.LoginRequest 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewLoginLogic(r.Context(), svcCtx) 21 | resp, err := l.Login(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/api/auth/internal/handler/captchahandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/auth/internal/logic" 8 | "micro/api/auth/internal/svc" 9 | "micro/api/auth/internal/types" 10 | ) 11 | 12 | func CaptchaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.EmptyRequest 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewCaptchaLogic(r.Context(), svcCtx) 21 | resp, err := l.Captcha(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/api/auth/desc/auth.api: -------------------------------------------------------------------------------- 1 | syntax = "v1" 2 | 3 | info( 4 | title: "登录鉴权" 5 | desc: "登录鉴权接口" 6 | author: "Xi-Yuer" 7 | email: "2214380963@qq.com" 8 | version: "v1" 9 | ) 10 | 11 | type ( 12 | LoginRequest { 13 | Account string `form:"account"` 14 | Password string `form:"password"` 15 | Captcha string `form:"captcha"` 16 | SessionID string `form:"sessionId"` 17 | } 18 | CommonResponse { 19 | Code int64 `json:"code"` 20 | Data interface{} `json:"data"` 21 | Msg string `json:"msg"` 22 | } 23 | EmptyRequest { 24 | } 25 | ) 26 | 27 | @server( 28 | prefix: /v1/auth 29 | ) 30 | 31 | service auth { 32 | @handler Login 33 | post /login (LoginRequest) returns (CommonResponse) 34 | 35 | @handler Captcha 36 | get /captcha (EmptyRequest) returns (CommonResponse) 37 | } -------------------------------------------------------------------------------- /micro/api/test/internal/handler/userinfohandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/test/internal/logic" 8 | "micro/api/test/internal/svc" 9 | "micro/api/test/internal/types" 10 | ) 11 | 12 | func userInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.UserInfoReq 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewUserInfoLogic(r.Context(), svcCtx) 21 | resp, err := l.UserInfo(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/shared/snowflake/snowflake.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bwmarrin/snowflake" 6 | "time" 7 | ) 8 | 9 | var Node *snowflake.Node 10 | 11 | func Init(startTime string, machineID int64) (err error) { 12 | var st time.Time 13 | // 格式化 1月2号下午3时4分5秒 2006年 14 | st, err = time.Parse("2006-01-02", startTime) 15 | if err != nil { 16 | return 17 | } 18 | 19 | snowflake.Epoch = st.UnixNano() / 1e6 20 | Node, err = snowflake.NewNode(machineID) 21 | if err != nil { 22 | return 23 | } 24 | 25 | return 26 | } 27 | 28 | func init() { 29 | if err := Init("2024-01-01", 1); err != nil { 30 | fmt.Println("Init() failed, err = ", err) 31 | return 32 | } 33 | } 34 | 35 | // GenID 生成 64 位的 雪花 ID 36 | func GenID() int64 { 37 | return Node.Generate().Int64() 38 | } 39 | -------------------------------------------------------------------------------- /server/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "github.com/Xi-Yuer/cms/config" 7 | "github.com/Xi-Yuer/cms/utils" 8 | _ "github.com/go-sql-driver/mysql" 9 | "time" 10 | ) 11 | 12 | var DB *sql.DB 13 | 14 | func InitDB() error { 15 | // 初始化数据库连接 16 | db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/cms?charset=utf8&parseTime=True", config.Config.DB.NAME, config.Config.DB.PASSWORD, config.Config.DB.HOST, config.Config.DB.PORT)) 17 | if err != nil { 18 | utils.Log.Panic(err) 19 | } 20 | 21 | DB = db 22 | 23 | DB.SetMaxOpenConns(20) 24 | DB.SetMaxIdleConns(10) 25 | DB.SetConnMaxLifetime(time.Minute * 60) 26 | err = DB.Ping() 27 | if err != nil { 28 | utils.Log.Panic(err) 29 | return err 30 | } 31 | utils.Log.Info("数据库连接成功") 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /micro/api/user/internal/handler/createuserhandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/user/internal/logic" 8 | "micro/api/user/internal/svc" 9 | "micro/api/user/internal/types" 10 | ) 11 | 12 | func CreateUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.CreateUserRequest 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewCreateUserLogic(r.Context(), svcCtx) 21 | resp, err := l.CreateUser(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/api/user/internal/handler/deleteuserhandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/user/internal/logic" 8 | "micro/api/user/internal/svc" 9 | "micro/api/user/internal/types" 10 | ) 11 | 12 | func DeleteUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.DeleteUserRequest 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewDeleteUserLogic(r.Context(), svcCtx) 21 | resp, err := l.DeleteUser(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/api/user/internal/handler/updateuserhandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/user/internal/logic" 8 | "micro/api/user/internal/svc" 9 | "micro/api/user/internal/types" 10 | ) 11 | 12 | func UpdateUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.UpdateUserRequest 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewUpdateUserLogic(r.Context(), svcCtx) 21 | resp, err := l.UpdateUser(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /micro/api/test/internal/logic/userinfologic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "micro/api/test/internal/svc" 8 | "micro/api/test/internal/types" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type UserInfoLogic struct { 14 | logx.Logger 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | } 18 | 19 | func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic { 20 | return &UserInfoLogic{ 21 | Logger: logx.WithContext(ctx), 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | } 25 | } 26 | 27 | func (l *UserInfoLogic) UserInfo(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) { 28 | value := l.ctx.Value("user_id") 29 | fmt.Println(value) 30 | return &types.UserInfoResp{ 31 | Name: "哈哈", 32 | }, nil 33 | } 34 | -------------------------------------------------------------------------------- /micro/api/user/internal/handler/getuserlisthandler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/zeromicro/go-zero/rest/httpx" 7 | "micro/api/user/internal/logic" 8 | "micro/api/user/internal/svc" 9 | "micro/api/user/internal/types" 10 | ) 11 | 12 | func GetUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { 13 | return func(w http.ResponseWriter, r *http.Request) { 14 | var req types.GetUserListRequest 15 | if err := httpx.Parse(r, &req); err != nil { 16 | httpx.ErrorCtx(r.Context(), w, err) 17 | return 18 | } 19 | 20 | l := logic.NewGetUserListLogic(r.Context(), svcCtx) 21 | resp, err := l.GetUserList(&req) 22 | if err != nil { 23 | httpx.ErrorCtx(r.Context(), w, err) 24 | } else { 25 | httpx.OkJsonCtx(r.Context(), w, resp) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/routers/modules/users/users.go: -------------------------------------------------------------------------------- 1 | package usersRouterModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/controllers" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func UseUserRoutes(r *gin.RouterGroup) { 9 | 10 | group := r.Group("/users") 11 | 12 | { 13 | group.POST("", controllers.UserController.CreateUser) 14 | group.GET("/:id", controllers.UserController.GetUser) 15 | group.POST("/query", controllers.UserController.GetUsers) 16 | group.POST("/query/role/:id", controllers.UserController.FindUserByParamsAndOutRoleID) 17 | group.GET("/role/:id", controllers.UserController.GetUserByRoleID) 18 | group.PATCH("/:id", controllers.UserController.UpdateUser) 19 | group.DELETE("/:id", controllers.UserController.DeleteUser) 20 | group.POST("/export", controllers.UserController.ExportExcel) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/middlewares/middlewares.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | authMiddleWareModule "github.com/Xi-Yuer/cms/middlewares/modules/auth" 5 | corsMiddlewareModule "github.com/Xi-Yuer/cms/middlewares/modules/cors" 6 | systemLogsMiddlewareModule "github.com/Xi-Yuer/cms/middlewares/modules/logs" 7 | ) 8 | import sessionMiddleWareModule "github.com/Xi-Yuer/cms/middlewares/modules/session" 9 | 10 | var SessionMiddleWareModule = sessionMiddleWareModule.Session 11 | 12 | var AuthMiddleWareModule = authMiddleWareModule.AuthTokenMiddleWare 13 | 14 | var LogsMiddlewareModule = systemLogsMiddlewareModule.SystemLogMiddlewareModule 15 | 16 | var AuthMethodMiddleWare = authMiddleWareModule.AuthMethodMiddleWare 17 | 18 | var AuthVerifyCookie = authMiddleWareModule.AuthVerifyCookie 19 | 20 | var Cors = corsMiddlewareModule.Cors 21 | -------------------------------------------------------------------------------- /web/src/store/UploadStore/index.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | export interface IUploadFile { 4 | fileName: string; 5 | fileSize: number; 6 | identifier: string; 7 | progress: number; 8 | } 9 | 10 | interface IUploadStore { 11 | uploadList: IUploadFile[]; 12 | } 13 | 14 | const useUploadStore = createSlice({ 15 | name: 'uploadStore', 16 | initialState: { 17 | uploadList: [], 18 | } as IUploadStore, 19 | reducers: { 20 | addUploadFile: (state, action) => { 21 | state.uploadList.push(action.payload); 22 | }, 23 | updateUploadFileList: (state, action) => { 24 | state.uploadList = action.payload; 25 | }, 26 | }, 27 | }); 28 | 29 | export default useUploadStore.reducer; 30 | export const { addUploadFile, updateUploadFileList } = useUploadStore.actions; 31 | -------------------------------------------------------------------------------- /server/dto/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package logsResponsiesModules 2 | 3 | type CreateLogRecordRequest struct { 4 | ID int64 5 | UserID string 6 | UserName string 7 | UserIP string 8 | RequestPath string 9 | RequestMethod string 10 | RequestStatus int 11 | RequestDuration string 12 | CreateTime int64 13 | } 14 | 15 | type GetLogRecordResponse struct { 16 | ID string `json:"id"` 17 | UserID string `json:"userID"` 18 | UserName string `json:"userName"` 19 | UserIP string `json:"userIP"` 20 | RequestPath string `json:"requestPath"` 21 | RequestMethod string `json:"requestMethod"` 22 | RequestStatus string `json:"requestStatus"` 23 | RequestDuration string `json:"requestDuration"` 24 | CreateTime string `json:"createTime"` 25 | } 26 | -------------------------------------------------------------------------------- /web/src/components/AppBreadcrumb/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo, useEffect, useState } from 'react'; 2 | import { useAppSelector } from '@/store'; 3 | import { menuType } from '@/types/menus'; 4 | import { getTheCurrentRoutePathAllMenuEntity } from '@/utils'; 5 | import { useLocation } from 'react-router-dom'; 6 | import { Breadcrumb } from 'antd'; 7 | 8 | const AppBreadcrumb: FC = () => { 9 | const { menus } = useAppSelector((state) => state.UserStore); 10 | const { pathname } = useLocation(); 11 | const [breadCrumb, setBreadCrumb] = useState([]); 12 | 13 | useEffect(() => { 14 | setBreadCrumb(getTheCurrentRoutePathAllMenuEntity(pathname, menus)); 15 | }, [menus, pathname]); 16 | return ({ title: item.pageName }))} className='font-bold select-none' />; 17 | }; 18 | 19 | export default memo(AppBreadcrumb); 20 | -------------------------------------------------------------------------------- /micro/api/test/internal/logic/loginlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "micro/shared/token" 6 | 7 | "micro/api/test/internal/svc" 8 | "micro/api/test/internal/types" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type LoginLogic struct { 14 | logx.Logger 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | } 18 | 19 | func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic { 20 | return &LoginLogic{ 21 | Logger: logx.WithContext(ctx), 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | } 25 | } 26 | 27 | func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginResp, err error) { 28 | createToken, err := token.CreateToken(100, "xiyuer") 29 | if err != nil { 30 | return nil, err 31 | } 32 | return &types.LoginResp{ 33 | ID: "1", 34 | Name: createToken, 35 | }, nil 36 | } 37 | -------------------------------------------------------------------------------- /server/middlewares/modules/cors/cors.go: -------------------------------------------------------------------------------- 1 | package corsMiddlewareModule 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | func Cors(context *gin.Context) { 9 | method := context.Request.Method 10 | context.Header("Access-Control-Allow-Origin", "*") 11 | context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") 12 | context.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") 13 | context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") 14 | context.Header("Access-Control-Allow-Credentials", "true") 15 | // 放行所有OPTIONS方法 16 | if method == "OPTIONS" { 17 | context.AbortWithStatus(http.StatusNoContent) 18 | return 19 | } 20 | context.Next() 21 | } 22 | -------------------------------------------------------------------------------- /micro/sql/init-slave.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Wait for the master to be fully up and running 5 | until mysql -h mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "select 1"; do 6 | >&2 echo "MySQL master is unavailable - sleeping" 7 | sleep 10 8 | done 9 | 10 | # Get master status 11 | MASTER_STATUS=$(mysql -h mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "SHOW MASTER STATUS\G") 12 | CURRENT_LOG=$(echo "$MASTER_STATUS" | grep "File:" | awk '{print $2}') 13 | CURRENT_POS=$(echo "$MASTER_STATUS" | grep "Position:" | awk '{print $2}') 14 | 15 | # Configure replication 16 | mysql -u root -p${MYSQL_ROOT_PASSWORD} < { 8 | const { t } = useTranslation(); 9 | const options = Object.keys(AllIcons) 10 | .slice(0, 300) 11 | .map((key: any) => { 12 | return { 13 | value: key, 14 | label: ( 15 | <> 16 | 17 | {key} 18 | 19 | ), 20 | }; 21 | }); 22 | return ( 23 | 24 | 25 | 26 | ); 27 | }; 28 | 29 | export default memo(IconSelect); 30 | -------------------------------------------------------------------------------- /web/src/components/index.ts: -------------------------------------------------------------------------------- 1 | import { DarkSvg } from '@/components/Icon/dark.tsx'; 2 | import { LightSvg } from '@/components/Icon/light.tsx'; 3 | import Icon from '@/components/Icon/icon.tsx'; 4 | import IconSelect from '@/components/IconSelect'; 5 | import TranslateDark from '@/components/Icon/translateDark.tsx'; 6 | import TranslateLight from '@/components/Icon/translateLight.tsx'; 7 | import ThemeBar from '@/components/Theme'; 8 | import Translate from '@/components/Translate/translate.tsx'; 9 | import AppBreadcrumb from '@/components/AppBreadcrumb'; 10 | import AppHeaderTab from '@/components/AppHeaderTab'; 11 | import Card from '@/components/Card'; 12 | import AppUploads from '@/components/AppUploads'; 13 | import Footer from '@/components/Footer'; 14 | 15 | export { DarkSvg, LightSvg, Icon, IconSelect, TranslateLight, TranslateDark, ThemeBar, Translate, AppBreadcrumb, AppHeaderTab, Card, AppUploads, Footer }; 16 | -------------------------------------------------------------------------------- /web/src/service/request/lib/type.ts: -------------------------------------------------------------------------------- 1 | import type { AxiosInterceptorOptions, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'; // 扩展拦截器 2 | 3 | export interface RequestConfig extends AxiosRequestConfig { 4 | requestInterceptor?: { 5 | onFulfilled?: (value: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise; 6 | onRejected?: ((error: any) => any) | null; 7 | options?: AxiosInterceptorOptions; 8 | }; 9 | 10 | responseInterceptor?: { 11 | onFulfilled?: (value: AxiosResponse) => AxiosResponse; 12 | onRejected?: ((error: any) => any) | null; 13 | options?: AxiosInterceptorOptions; 14 | }; 15 | } 16 | 17 | export type InterceptorType = Partial>; 18 | 19 | export interface IResponse { 20 | data: T; 21 | message: string; 22 | code: number; 23 | } 24 | -------------------------------------------------------------------------------- /server/services/modules/rolesAndInterfaces/rolesAndInterfaces.go: -------------------------------------------------------------------------------- 1 | package rolesAndInterfacesServiceModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/dto" 5 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 6 | ) 7 | 8 | var RolesAndInterfacesService = &rolesAndInterfacesService{} 9 | 10 | type rolesAndInterfacesService struct{} 11 | 12 | // GetInterfacesByRoleID 根据角色ID获取接口 13 | func (r *rolesAndInterfacesService) GetInterfacesByRoleID(roleId string) ([]*dto.GetInterfaceResponse, error) { 14 | interIDs, err := repositories.RolesAndInterfacesRepository.GetRecordByRoleID(roleId) 15 | if err != nil { 16 | return nil, err 17 | } 18 | var interfaceDic []*dto.GetInterfaceResponse 19 | for _, id := range interIDs { 20 | inter, e := repositories.InterfaceRepository.GetInterfaceByID(id) 21 | if !e { 22 | continue 23 | } 24 | interfaceDic = append(interfaceDic, inter) 25 | } 26 | return interfaceDic, nil 27 | } 28 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": [ 6 | "ES2020", 7 | "DOM", 8 | "DOM.Iterable", 9 | "ESNext" 10 | ], 11 | "module": "ESNext", 12 | "skipLibCheck": true, 13 | /* Bundler mode */ 14 | "moduleResolution": "bundler", 15 | "allowImportingTsExtensions": true, 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true, 25 | "baseUrl": ".", 26 | "paths": { 27 | "@/*": [ 28 | "src/*" 29 | ] 30 | } 31 | }, 32 | "include": [ 33 | "src" 34 | ], 35 | "references": [ 36 | { 37 | "path": "./tsconfig.node.json" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /server/utils/modules/snowflake/snowflake.go: -------------------------------------------------------------------------------- 1 | package snowflake 2 | 3 | import ( 4 | "fmt" 5 | logsModules "github.com/Xi-Yuer/cms/utils/modules/logs" 6 | "github.com/bwmarrin/snowflake" 7 | "time" 8 | ) 9 | 10 | var Node *snowflake.Node 11 | 12 | func Init(startTime string, machineID int64) (err error) { 13 | var st time.Time 14 | // 格式化 1月2号下午3时4分5秒 2006年 15 | st, err = time.Parse("2006-01-02", startTime) 16 | if err != nil { 17 | logsModules.Log.Error(err) 18 | return 19 | } 20 | 21 | snowflake.Epoch = st.UnixNano() / 1e6 22 | Node, err = snowflake.NewNode(machineID) 23 | if err != nil { 24 | logsModules.Log.Error(err) 25 | return 26 | } 27 | 28 | return 29 | } 30 | 31 | func init() { 32 | if err := Init("2024-01-01", 1); err != nil { 33 | fmt.Println("Init() failed, err = ", err) 34 | return 35 | } 36 | } 37 | 38 | // GenID 生成 64 位的 雪花 ID 39 | func GenID() int64 { 40 | return Node.Generate().Int64() 41 | } 42 | -------------------------------------------------------------------------------- /server/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | var Config = config{ 4 | APP: APP{ 5 | PORT: ":8081", 6 | SESSIONSECRET: "FUCNdjYGFg4G", 7 | JWT: "P8BkI2OpBkY", 8 | SWAGPATH: "http://localhost:8081/swagger/docs/index.html#/example", 9 | BASEURL: "/cms", 10 | FILEPATH: "./uploadFile/", 11 | DOMAIN: "localhost", 12 | }, 13 | DB: DB{ 14 | NAME: "root", 15 | PASSWORD: "2214380963Wx!!", 16 | HOST: "localhost", 17 | DB: "cms", 18 | PORT: "3306", 19 | }, 20 | } 21 | 22 | type config struct { 23 | APP 24 | DB 25 | } 26 | 27 | type APP struct { 28 | PORT string 29 | SESSIONSECRET string 30 | JWT string 31 | SWAGPATH string 32 | BASEURL string 33 | FILEPATH string 34 | DOMAIN string 35 | } 36 | type DB struct { 37 | NAME string 38 | PASSWORD string 39 | HOST string 40 | DB string 41 | PORT string 42 | } 43 | -------------------------------------------------------------------------------- /server/utils/modules/file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var File = &file{} 9 | 10 | type file struct{} 11 | 12 | // PathExists 检查文件是否存在 13 | func (f *file) PathExists(path string) bool { 14 | _, err := os.Stat(path) 15 | if err == nil { 16 | return true 17 | } 18 | if os.IsNotExist(err) { 19 | return false 20 | } 21 | return false 22 | } 23 | 24 | // CheckOrCreateFolder 检查文件夹是否存在 25 | func (f *file) CheckOrCreateFolder(folderPath string) error { 26 | // 检查文件夹是否存在 27 | _, err := os.Stat(folderPath) 28 | if os.IsNotExist(err) { 29 | // 文件夹不存在,创建文件夹 30 | err := os.MkdirAll(folderPath, 0755) 31 | if err != nil { 32 | return fmt.Errorf("创建文件夹失败: %v", err) 33 | } 34 | //fmt.Println("文件夹不存在,已创建:", folderPath) 35 | } else if err != nil { 36 | // 其他错误,返回错误信息 37 | return fmt.Errorf("检查文件夹失败: %v", err) 38 | } else { 39 | //fmt.Println("文件夹已存在:", folderPath) 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /server/controllers/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package logsControllerModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/dto" 5 | "github.com/Xi-Yuer/cms/services" 6 | "github.com/Xi-Yuer/cms/utils" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | var LogsController = &logsControllerModules{} 11 | 12 | type logsControllerModules struct{} 13 | 14 | // GetLogRecords 获取系统日志 15 | // @Summary 获取系统日志 16 | // @Description 获取系统日志 17 | // @Tags 日志管理 18 | // @Accept json 19 | // @Produce json 20 | // @Router /log/system [get] 21 | func (l *logsControllerModules) GetLogRecords(context *gin.Context) { 22 | var params dto.Page 23 | if err := context.ShouldBind(¶ms); err != nil { 24 | utils.Response.ParameterMissing(context, err.Error()) 25 | return 26 | } 27 | logRecords, err := services.LogsService.GetLogRecords(¶ms) 28 | if err != nil { 29 | utils.Response.ServerError(context, err.Error()) 30 | return 31 | } 32 | utils.Response.Success(context, logRecords) 33 | } 34 | -------------------------------------------------------------------------------- /micro/api/user/internal/handler/routes.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | package handler 3 | 4 | import ( 5 | "net/http" 6 | 7 | "micro/api/user/internal/svc" 8 | 9 | "github.com/zeromicro/go-zero/rest" 10 | ) 11 | 12 | func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { 13 | server.AddRoutes( 14 | []rest.Route{ 15 | { 16 | Method: http.MethodGet, 17 | Path: "/", 18 | Handler: GetUserListHandler(serverCtx), 19 | }, 20 | { 21 | Method: http.MethodDelete, 22 | Path: "/:id", 23 | Handler: DeleteUserHandler(serverCtx), 24 | }, 25 | { 26 | Method: http.MethodPut, 27 | Path: "/:id", 28 | Handler: UpdateUserHandler(serverCtx), 29 | }, 30 | { 31 | Method: http.MethodPost, 32 | Path: "/", 33 | Handler: CreateUserHandler(serverCtx), 34 | }, 35 | }, 36 | rest.WithJwt(serverCtx.Config.Auth.AccessSecret), 37 | rest.WithPrefix("/v1/user"), 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /micro/api/test/user.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net/http" 7 | 8 | "micro/api/test/internal/config" 9 | "micro/api/test/internal/handler" 10 | "micro/api/test/internal/svc" 11 | 12 | "github.com/zeromicro/go-zero/core/conf" 13 | "github.com/zeromicro/go-zero/rest" 14 | ) 15 | 16 | var configFile = flag.String("f", "etc/user-api.yaml", "the config file") 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | var c config.Config 22 | conf.MustLoad(*configFile, &c) 23 | 24 | server := rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(func(w http.ResponseWriter, r *http.Request, err error) { 25 | // 自定义处理返回 26 | w.WriteHeader(http.StatusUnauthorized) 27 | http.Error(w, "Unauthorized", http.StatusUnauthorized) 28 | })) 29 | defer server.Stop() 30 | 31 | ctx := svc.NewServiceContext(c) 32 | handler.RegisterHandlers(server, ctx) 33 | 34 | fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port) 35 | server.Start() 36 | } 37 | -------------------------------------------------------------------------------- /server/dto/modules/upload/upload.go: -------------------------------------------------------------------------------- 1 | package uploadResponsiesModules 2 | 3 | import "mime/multipart" 4 | 5 | type UploadBigFileRequest struct { 6 | Identifier string `form:"identifier" binding:"required"` 7 | UpFile *multipart.FileHeader `form:"file" binding:"required"` 8 | } 9 | 10 | type CheckChunkResponse struct { 11 | HasReadySize int64 `json:"hasReadySize"` 12 | } 13 | 14 | type CheckChunkRequest struct { 15 | Identifier string `form:"identifier" binding:"required"` 16 | } 17 | 18 | type UploadFinishRequest struct { 19 | Identifier string `form:"identifier" binding:"required"` 20 | FileName string `form:"fileName" binding:"required"` 21 | FileSize int64 `form:"fileSize" binding:"required"` 22 | } 23 | type UploadRecordResponse struct { 24 | FileID string `json:"fileID"` 25 | FileName string `json:"fileName"` 26 | FileSize int64 `json:"fileSize"` 27 | UploadUser string `json:"uploadUser"` 28 | UploadTime string `json:"uploadTime"` 29 | } 30 | -------------------------------------------------------------------------------- /web/src/service/api/login/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { Md5 } from 'ts-md5'; 3 | import { AxiosResponse } from 'axios'; 4 | 5 | export type LoginParamsType = { 6 | account: string; 7 | password: string; 8 | captcha: string; 9 | }; 10 | 11 | export interface LoginUserResponseType { 12 | id: string; 13 | account: string; 14 | nickname: string; 15 | isAdmin: number; 16 | avatar: null; 17 | rolesID: string[]; 18 | interfaceDic: string[]; 19 | departmentID: string; 20 | createTime: Date; 21 | updateTime: Date; 22 | status: string; 23 | } 24 | 25 | export const loginRequest = (data: LoginParamsType) => { 26 | return request.post>({ 27 | url: '/auth/login', 28 | data: { 29 | ...data, 30 | password: Md5.hashStr(data.password), 31 | }, 32 | }); 33 | }; 34 | 35 | export const getCaptchaRequest = () => { 36 | return request.get({ 37 | url: '/auth/captcha', 38 | responseType: 'blob', 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /server/dto/modules/timeTask/timeTask.go: -------------------------------------------------------------------------------- 1 | package timeTaskResponsiesModules 2 | 3 | type StartTimeTack struct { 4 | TimeTaskID string `form:"timeTaskID" binding:"required"` 5 | } 6 | 7 | type StopTimeTack struct { 8 | TimeTaskID string `form:"timeTaskID" binding:"required"` 9 | } 10 | 11 | type DeleteTimeTack struct { 12 | TimeTaskID string `form:"timeTaskID" binding:"required"` 13 | } 14 | 15 | type CreateTimeTack struct { 16 | TaskName string `form:"taskName" binding:"required"` 17 | Cron string `form:"cron" binding:"required"` 18 | } 19 | 20 | type UpdateTimeTask struct { 21 | TaskName string `form:"taskName" binding:"required"` 22 | Cron string `form:"cron" binding:"required"` 23 | Status *bool `form:"status" binding:"required"` 24 | } 25 | 26 | type TimeTaskResponse struct { 27 | TimeTaskID string `json:"timeTaskID"` 28 | TaskName string `json:"taskName"` 29 | Cron string `json:"cron"` 30 | Status bool `json:"status"` 31 | LastRunTime string `json:"lastRunTime"` 32 | RunTimes float64 `json:"runTimes"` 33 | } 34 | -------------------------------------------------------------------------------- /web/src/utils/event/index.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import { RcFile } from 'antd/es/upload'; 3 | 4 | const eventEmitter = new EventEmitter(); 5 | 6 | // 组件间通讯的事件 7 | export const EVENT_UPLOAD_FILE = 'uploadFile'; 8 | export const GET_FILE_LIST = 'getFileList'; 9 | 10 | // 注册事件监听 11 | export function addListenerUploadFile(handler: (file: RcFile) => void) { 12 | eventEmitter.on(EVENT_UPLOAD_FILE, handler); 13 | } 14 | 15 | export function addListenerGetFileList(handler: () => void) { 16 | eventEmitter.on(GET_FILE_LIST, handler); 17 | } 18 | 19 | export function removeListenerGetFileList(handler: () => void) { 20 | eventEmitter.removeListener(GET_FILE_LIST, handler); 21 | } 22 | 23 | export function removeListenerUploadFile(handler: (file: RcFile) => void) { 24 | eventEmitter.removeListener(EVENT_UPLOAD_FILE, handler); 25 | } 26 | 27 | // 触发事件 28 | export function emitUploadFile(file: RcFile) { 29 | eventEmitter.emit(EVENT_UPLOAD_FILE, file); 30 | } 31 | 32 | export function emitGetFileList() { 33 | eventEmitter.emit(GET_FILE_LIST); 34 | } 35 | -------------------------------------------------------------------------------- /micro/rpc/role/role.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "micro/rpc/role/internal/config" 8 | "micro/rpc/role/internal/server" 9 | "micro/rpc/role/internal/svc" 10 | "micro/rpc/role/roleRPC" 11 | 12 | "github.com/zeromicro/go-zero/core/conf" 13 | "github.com/zeromicro/go-zero/core/service" 14 | "github.com/zeromicro/go-zero/zrpc" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | var configFile = flag.String("f", "etc/role.yaml", "the config file") 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | var c config.Config 25 | conf.MustLoad(*configFile, &c) 26 | ctx := svc.NewServiceContext(c) 27 | 28 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 29 | roleRPC.RegisterRoleServiceServer(grpcServer, server.NewRoleServiceServer(ctx)) 30 | 31 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 32 | reflection.Register(grpcServer) 33 | } 34 | }) 35 | defer s.Stop() 36 | 37 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) 38 | s.Start() 39 | } 40 | -------------------------------------------------------------------------------- /micro/rpc/user/user.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "micro/rpc/user/internal/config" 8 | "micro/rpc/user/internal/server" 9 | "micro/rpc/user/internal/svc" 10 | "micro/rpc/user/userRPC" 11 | 12 | "github.com/zeromicro/go-zero/core/conf" 13 | "github.com/zeromicro/go-zero/core/service" 14 | "github.com/zeromicro/go-zero/zrpc" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | var configFile = flag.String("f", "etc/user.yaml", "the config file") 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | var c config.Config 25 | conf.MustLoad(*configFile, &c) 26 | ctx := svc.NewServiceContext(c) 27 | 28 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 29 | userRPC.RegisterUserServiceServer(grpcServer, server.NewUserServiceServer(ctx)) 30 | 31 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 32 | reflection.Register(grpcServer) 33 | } 34 | }) 35 | defer s.Stop() 36 | 37 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) 38 | s.Start() 39 | } 40 | -------------------------------------------------------------------------------- /micro/rpc/captcha/internal/server/captchaserviceserver.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: captcha.proto 3 | 4 | package server 5 | 6 | import ( 7 | "context" 8 | 9 | "micro/rpc/captcha/captcha" 10 | "micro/rpc/captcha/internal/logic" 11 | "micro/rpc/captcha/internal/svc" 12 | ) 13 | 14 | type CaptchaServiceServer struct { 15 | svcCtx *svc.ServiceContext 16 | captcha.UnimplementedCaptchaServiceServer 17 | } 18 | 19 | func NewCaptchaServiceServer(svcCtx *svc.ServiceContext) *CaptchaServiceServer { 20 | return &CaptchaServiceServer{ 21 | svcCtx: svcCtx, 22 | } 23 | } 24 | 25 | func (s *CaptchaServiceServer) GenerateCaptcha(ctx context.Context, in *captcha.GenerateCaptchaRequest) (*captcha.GenerateCaptchaResponse, error) { 26 | l := logic.NewGenerateCaptchaLogic(ctx, s.svcCtx) 27 | return l.GenerateCaptcha(in) 28 | } 29 | 30 | func (s *CaptchaServiceServer) VerifyCaptcha(ctx context.Context, in *captcha.VerifyCaptchaRequest) (*captcha.VerifyCaptchaResponse, error) { 31 | l := logic.NewVerifyCaptchaLogic(ctx, s.svcCtx) 32 | return l.VerifyCaptcha(in) 33 | } 34 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/useridhasbeenexistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | 7 | "micro/rpc/user/internal/svc" 8 | "micro/rpc/user/userRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type UserIDHasBeenExistLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewUserIDHasBeenExistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserIDHasBeenExistLogic { 20 | return &UserIDHasBeenExistLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *UserIDHasBeenExistLogic) UserIDHasBeenExist(in *userRPC.DeleteUserRequest) (*userRPC.CommonResponse, error) { 28 | find := l.svcCtx.GormDB.Model(&userModel.User{}).Find(&userModel.User{}, "id = ?", in.Id) 29 | if find.RowsAffected > 0 { 30 | return &userRPC.CommonResponse{ 31 | Ok: true, 32 | Msg: "", 33 | }, nil 34 | } 35 | 36 | return &userRPC.CommonResponse{ 37 | Ok: false, 38 | Msg: "用户不存在", 39 | }, nil 40 | } 41 | -------------------------------------------------------------------------------- /server/services/modules/usersAndRoles/usersAndRoles.go: -------------------------------------------------------------------------------- 1 | package usersAndRolesServiceModules 2 | 3 | import ( 4 | "errors" 5 | "github.com/Xi-Yuer/cms/dto" 6 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 7 | ) 8 | 9 | var UserAndRolesService = &userAndRolesService{} 10 | 11 | type userAndRolesService struct { 12 | } 13 | 14 | func (u *userAndRolesService) FindRoleById(id string) error { 15 | singleRoleResponse := repositories.RoleRepositorysModules.FindRoleById(id) 16 | if singleRoleResponse == nil { 17 | return errors.New("资源不存在") 18 | } 19 | return nil 20 | } 21 | 22 | func (u *userAndRolesService) GetRoles(params *dto.QueryRolesParams) (*dto.HasTotalResponseData, error) { 23 | return repositories.RoleRepositorysModules.GetRoles(params) 24 | } 25 | 26 | func (u *userAndRolesService) UpdateRole(role *dto.UpdateRoleParams, id string) error { 27 | singleRoleResponse := repositories.RoleRepositorysModules.FindRoleById(id) 28 | if singleRoleResponse == nil { 29 | return errors.New("资源不存在") 30 | } 31 | return repositories.RoleRepositorysModules.UpdateRole(role, id) 32 | } 33 | -------------------------------------------------------------------------------- /micro/rpc/captcha/captcha.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | 7 | "micro/rpc/captcha/captcha" 8 | "micro/rpc/captcha/internal/config" 9 | "micro/rpc/captcha/internal/server" 10 | "micro/rpc/captcha/internal/svc" 11 | 12 | "github.com/zeromicro/go-zero/core/conf" 13 | "github.com/zeromicro/go-zero/core/service" 14 | "github.com/zeromicro/go-zero/zrpc" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | var configFile = flag.String("f", "etc/captcha.yaml", "the config file") 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | var c config.Config 25 | conf.MustLoad(*configFile, &c) 26 | ctx := svc.NewServiceContext(c) 27 | 28 | s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { 29 | captcha.RegisterCaptchaServiceServer(grpcServer, server.NewCaptchaServiceServer(ctx)) 30 | 31 | if c.Mode == service.DevMode || c.Mode == service.TestMode { 32 | reflection.Register(grpcServer) 33 | } 34 | }) 35 | defer s.Stop() 36 | 37 | fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) 38 | s.Start() 39 | } 40 | -------------------------------------------------------------------------------- /web/src/service/api/timeTask/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export interface ITimeTaskResponse { 5 | cron: string; 6 | lastRunTime: string; 7 | runTimes: number; 8 | status: boolean; 9 | taskName: string; 10 | timeTaskID: string; 11 | } 12 | 13 | export const getTimeTaskListRequest = () => { 14 | return request.get>({ 15 | url: '/timeTask', 16 | }); 17 | }; 18 | 19 | export const startTimeTaskRequest = (id: string) => { 20 | return request.post({ 21 | url: `/timeTask/start/${id}`, 22 | }); 23 | }; 24 | 25 | export const stopTimeTaskRequest = (id: string) => { 26 | return request.post({ 27 | url: `/timeTask/stop/${id}`, 28 | }); 29 | }; 30 | 31 | export interface IUpdateTimeTaskRequest { 32 | timeTaskName: string; 33 | cron: string; 34 | status: boolean; 35 | } 36 | 37 | export const updateTimeTaskRequest = (id: string, data: IUpdateTimeTaskRequest) => { 38 | return request.patch({ 39 | url: `/timeTask/update/${id}`, 40 | data, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /web/src/service/api/pages/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { menuType } from '@/types/menus'; 3 | import { AxiosResponse } from 'axios'; 4 | 5 | export const getUserMenusRequest = () => { 6 | return request.get>({ 7 | url: '/pages/user', 8 | }); 9 | }; 10 | 11 | export const getRoleMenusRequest = (id: string) => { 12 | return request.get>({ 13 | url: `/pages/role/${id}`, 14 | }); 15 | }; 16 | 17 | export const getAllMenusRequest = () => { 18 | return request.get>({ 19 | url: '/pages', 20 | }); 21 | }; 22 | 23 | export const deleteMenuRequest = (id: string) => { 24 | return request.delete({ 25 | url: `/pages/${id}`, 26 | }); 27 | }; 28 | 29 | export const updateMenuRequest = (id: string, data: menuType) => { 30 | return request.patch({ 31 | url: `/pages/${id}`, 32 | data, 33 | }); 34 | }; 35 | 36 | export const createMenuRequest = (data: menuType) => { 37 | return request.post({ 38 | url: '/pages', 39 | data, 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/createrolelogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | roleModlel "micro/model/role" 6 | "micro/rpc/role/internal/svc" 7 | "micro/rpc/role/roleRPC" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type CreateRoleLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewCreateRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRoleLogic { 19 | return &CreateRoleLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | func (l *CreateRoleLogic) CreateRole(in *roleRPC.CreateRoleRequest) (*roleRPC.CommonResponse, error) { 27 | role := &roleModlel.Role{ 28 | ID: in.Id, 29 | RoleName: in.RoleName, 30 | Description: in.Description, 31 | CanEdit: in.CanEdit, 32 | } 33 | if err := l.svcCtx.GormDB.Create(role).Error; err != nil { 34 | return &roleRPC.CommonResponse{ 35 | Ok: false, 36 | Msg: err.Error(), 37 | }, nil 38 | } 39 | 40 | return &roleRPC.CommonResponse{ 41 | Ok: true, 42 | Msg: "创建成功", 43 | }, nil 44 | } 45 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/roleidshasbeenexistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | roleModlel "micro/model/role" 7 | "micro/rpc/role/internal/svc" 8 | "micro/rpc/role/roleRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type RoleIDsHasBeenExistLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewRoleIDsHasBeenExistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RoleIDsHasBeenExistLogic { 20 | return &RoleIDsHasBeenExistLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *RoleIDsHasBeenExistLogic) RoleIDsHasBeenExist(in *roleRPC.RoleIDsHasBeenExistRequest) (*roleRPC.CommonResponse, error) { 28 | var roles []roleModlel.Role 29 | if err := l.svcCtx.GormDB.Where("id in (?)", in.Ids).Find(&roles).Error; err != nil { 30 | return nil, err 31 | } 32 | 33 | if len(roles) != len(in.Ids) { 34 | return nil, errors.New("角色ID不存在") 35 | } 36 | 37 | return &roleRPC.CommonResponse{ 38 | Ok: true, 39 | Msg: "", 40 | }, nil 41 | 42 | } 43 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/useraccounthasbeenexistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | 7 | "micro/rpc/user/internal/svc" 8 | "micro/rpc/user/userRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type UserAccountHasBeenExistLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewUserAccountHasBeenExistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserAccountHasBeenExistLogic { 20 | return &UserAccountHasBeenExistLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *UserAccountHasBeenExistLogic) UserAccountHasBeenExist(in *userRPC.UserAccountHasBeenExistRequest) (*userRPC.CommonResponse, error) { 28 | find := l.svcCtx.GormDB.Model(&userModel.User{}).Find(&userModel.User{}, "account = ?", in.Account) 29 | if find.RowsAffected > 0 { 30 | return &userRPC.CommonResponse{ 31 | Ok: true, 32 | Msg: "账号已存在", 33 | }, nil 34 | } 35 | 36 | return &userRPC.CommonResponse{ 37 | Ok: false, 38 | Msg: "账号不存在", 39 | }, nil 40 | } 41 | -------------------------------------------------------------------------------- /web/src/main.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client'; 2 | import '@/utils/echarts'; 3 | import 'animate.css'; 4 | import '@/styles/index.css'; 5 | import '@/local/index'; 6 | import store from '@/store'; 7 | import { Provider } from 'react-redux'; 8 | import { PersistGate } from 'redux-persist/integration/react'; 9 | import { persistStore } from 'redux-persist'; 10 | import { HashRouter } from 'react-router-dom'; 11 | import { App as AntdApp, Image, Spin } from 'antd'; 12 | import App from '@/App.tsx'; 13 | import ErrorBoundary from 'antd/es/alert/ErrorBoundary'; 14 | import LoadingGIF from '@/assets/image/loading.gif'; 15 | 16 | ReactDOM.createRoot(document.getElementById('root')!).render( 17 | 18 | 19 | } style={{ width: '100px', height: '100px' }}>} 21 | persistor={persistStore(store)}> 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | , 30 | ); 31 | -------------------------------------------------------------------------------- /micro/api/user/internal/logic/deleteuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "micro/api/user/internal/svc" 6 | "micro/api/user/internal/types" 7 | "micro/rpc/user/userRPC" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type DeleteUserLogic struct { 13 | logx.Logger 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | } 17 | 18 | func NewDeleteUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteUserLogic { 19 | return &DeleteUserLogic{ 20 | Logger: logx.WithContext(ctx), 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | } 24 | } 25 | 26 | func (l *DeleteUserLogic) DeleteUser(req *types.DeleteUserRequest) (resp *types.DeleteUserResponse, err error) { 27 | response, err := l.svcCtx.UserService.DeleteUser(l.ctx, &userRPC.DeleteUserRequest{Id: req.ID}) 28 | if err != nil { 29 | return &types.DeleteUserResponse{ 30 | Code: 500, 31 | Msg: err.Error(), 32 | }, nil 33 | } 34 | if !response.Ok { 35 | return &types.DeleteUserResponse{ 36 | Code: 500, 37 | Msg: response.Msg, 38 | }, nil 39 | } 40 | return &types.DeleteUserResponse{ 41 | Code: 200, 42 | Msg: response.Msg, 43 | }, nil 44 | } 45 | -------------------------------------------------------------------------------- /micro/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | events { 3 | worker_connections 4096; 4 | } 5 | 6 | http { 7 | include mime.types; 8 | default_type application/octet-stream; 9 | 10 | sendfile on; 11 | keepalive_timeout 65; 12 | 13 | upstream auth_api { 14 | server auth-service:8888; 15 | } 16 | 17 | upstream user_api { 18 | server user-service:8889; 19 | } 20 | 21 | server { 22 | listen 80; 23 | server_name localhost; 24 | 25 | location /auth/ { 26 | proxy_pass http://auth_api/; 27 | proxy_set_header Host $host; 28 | proxy_set_header X-Real-IP $remote_addr; 29 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 30 | proxy_set_header X-Forwarded-Proto $scheme; 31 | } 32 | 33 | location /user/ { 34 | proxy_pass http://user_api/; 35 | proxy_set_header Host $host; 36 | proxy_set_header X-Real-IP $remote_addr; 37 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 38 | proxy_set_header X-Forwarded-Proto $scheme; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /web/src/assets/svg/en-zh.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 9 | -------------------------------------------------------------------------------- /web/src/assets/svg/zh-en.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 7 | 9 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/getuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | 7 | "micro/rpc/user/internal/svc" 8 | "micro/rpc/user/userRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type GetUserLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { 20 | return &GetUserLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *GetUserLogic) GetUser(in *userRPC.GetUserRequest) (*userRPC.GetUserResponse, error) { 28 | var user userModel.User 29 | if err := l.svcCtx.GormDB.Model(&userModel.User{}).First(&user, in.Id).Error; err != nil { 30 | return nil, err 31 | } 32 | return &userRPC.GetUserResponse{ 33 | Id: user.ID, 34 | Account: user.Account, 35 | Nickname: user.NickName, 36 | Avatar: user.Avatar, 37 | Department: user.DepartmentID, 38 | Status: user.Status, 39 | IsAdmin: user.IsAdmin, 40 | CreateTime: user.CreatedAt.String(), 41 | UpdateTime: user.UpdatedAt.String(), 42 | }, nil 43 | } 44 | -------------------------------------------------------------------------------- /web/src/styles/global/index.css: -------------------------------------------------------------------------------- 1 | .loginBg { 2 | background: url('@/assets/svg/bg.svg'); 3 | background-size: contain; 4 | } 5 | 6 | body { 7 | width: 100vw; 8 | height: 100vh; 9 | overflow: hidden; 10 | } 11 | 12 | @-webkit-keyframes fadeInRight { 13 | 0% { 14 | opacity: 0; 15 | -webkit-transform: translate3d(100%, 0, 0); 16 | transform: translate3d(50%, 0, 0); 17 | } 18 | to { 19 | opacity: 1; 20 | -webkit-transform: translateZ(0); 21 | transform: translateZ(0); 22 | } 23 | } 24 | 25 | @keyframes fadeInRight { 26 | 0% { 27 | opacity: 0; 28 | -webkit-transform: translate3d(100%, 0, 0); 29 | transform: translate3d(50%, 0, 0); 30 | } 31 | to { 32 | opacity: 1; 33 | -webkit-transform: translateZ(0); 34 | transform: translateZ(0); 35 | } 36 | } 37 | 38 | /* animations.css */ 39 | .enter { 40 | transform: translateY(200%); 41 | } 42 | 43 | .enter-active { 44 | transform: translateY(0%); 45 | transition: all 1s ease; 46 | } 47 | 48 | .exit { 49 | transform: translateY(0%); 50 | } 51 | 52 | .exit-active { 53 | transform: translateY(200%); 54 | transition: all 1s ease; 55 | } 56 | -------------------------------------------------------------------------------- /server/repositories/modules/commits/commits.go: -------------------------------------------------------------------------------- 1 | package commitsRepositoryModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/db" 5 | "github.com/Xi-Yuer/cms/dto" 6 | ) 7 | 8 | var CommitsRepositoryModules = &commitsRepositoryModules{} 9 | 10 | type commitsRepositoryModules struct{} 11 | 12 | func (c *commitsRepositoryModules) GetCommits() []*dto.CommitResponse { 13 | rows, err := db.DB.Query("SELECT commit_id, author, email, commit_date, message FROM cms.commits") 14 | if err != nil { 15 | return nil 16 | } 17 | var commitLogs []*dto.CommitResponse 18 | 19 | for rows.Next() { 20 | var commit dto.CommitResponse 21 | err := rows.Scan(&commit.CommitID, &commit.Author, &commit.Email, &commit.Date, &commit.Message) 22 | if err != nil { 23 | continue 24 | } else { 25 | commitLogs = append(commitLogs, &commit) 26 | } 27 | 28 | } 29 | return commitLogs 30 | } 31 | 32 | func (c *commitsRepositoryModules) GetCommitsCount() (int, error) { 33 | query := "SELECT COUNT(*) FROM cms.commits" 34 | rows, err := db.DB.Query(query) 35 | if err != nil { 36 | return 0, err 37 | } 38 | var count int 39 | for rows.Next() { 40 | err := rows.Scan(&count) 41 | if err != nil { 42 | return 0, err 43 | } 44 | } 45 | return count, nil 46 | } 47 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/rolenamehasbeenexistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | roleModlel "micro/model/role" 6 | 7 | "micro/rpc/role/internal/svc" 8 | "micro/rpc/role/roleRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type RoleNameHasBeenExistLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewRoleNameHasBeenExistLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RoleNameHasBeenExistLogic { 20 | return &RoleNameHasBeenExistLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *RoleNameHasBeenExistLogic) RoleNameHasBeenExist(in *roleRPC.RoleNamesHasBeenExistRequest) (*roleRPC.CommonResponse, error) { 28 | count := int64(0) 29 | if err := l.svcCtx.GormDB.Where("role_name = ?", in.Names).First(&roleModlel.Role{}).Count(&count).Error; err != nil { 30 | return &roleRPC.CommonResponse{ 31 | Ok: false, 32 | Msg: err.Error(), 33 | }, nil 34 | } 35 | if count > 0 { 36 | return &roleRPC.CommonResponse{ 37 | Ok: true, 38 | Msg: "角色名已存在", 39 | }, nil 40 | } 41 | return &roleRPC.CommonResponse{ 42 | Ok: false, 43 | Msg: "", 44 | }, nil 45 | } 46 | -------------------------------------------------------------------------------- /micro/api/user/internal/logic/updateuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "micro/api/user/internal/svc" 6 | "micro/api/user/internal/types" 7 | "micro/rpc/user/userRPC" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type UpdateUserLogic struct { 13 | logx.Logger 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | } 17 | 18 | func NewUpdateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserLogic { 19 | return &UpdateUserLogic{ 20 | Logger: logx.WithContext(ctx), 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | } 24 | } 25 | 26 | func (l *UpdateUserLogic) UpdateUser(req *types.UpdateUserRequest) (resp *types.UpdateUserResponse, err error) { 27 | response, _ := l.svcCtx.UserService.UpdateUser(l.ctx, &userRPC.UpdateUserRequest{ 28 | Id: req.ID, 29 | Password: req.Password, 30 | Nickname: req.Nickname, 31 | Avatar: req.Avatar, 32 | Status: req.Status, 33 | Department: req.DepartmentID, 34 | IsAdmin: req.IsAdmin, 35 | }) 36 | if response.Ok { 37 | return &types.UpdateUserResponse{ 38 | Code: 200, 39 | Msg: response.Msg, 40 | }, nil 41 | } 42 | return &types.UpdateUserResponse{ 43 | Code: 500, 44 | Msg: response.Msg, 45 | }, nil 46 | } 47 | -------------------------------------------------------------------------------- /micro/rpc/captcha/internal/logic/verifycaptchalogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "micro/rpc/captcha/captcha" 9 | "micro/rpc/captcha/internal/svc" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type VerifyCaptchaLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewVerifyCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VerifyCaptchaLogic { 21 | return &VerifyCaptchaLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | func (l *VerifyCaptchaLogic) VerifyCaptcha(in *captcha.VerifyCaptchaRequest) (*captcha.VerifyCaptchaResponse, error) { 29 | sessionID := in.GetSessionId() 30 | userInputCode := in.GetCaptchaCode() 31 | 32 | // 从 Redis 中获取存储的验证码 33 | storedCode, err := l.svcCtx.Redis.Get(l.ctx, fmt.Sprintf("captcha:%s", sessionID)).Result() 34 | if err != nil { 35 | return nil, errors.New("验证码已过期,请重新获取") 36 | } 37 | 38 | // 检查存储的验证码是否与用户输入的验证码匹配 39 | valid := storedCode == userInputCode 40 | 41 | // 删除 Redis 中存储的验证码 42 | l.svcCtx.Redis.Del(l.ctx, fmt.Sprintf("captcha:%s", sessionID)) 43 | 44 | return &captcha.VerifyCaptchaResponse{ 45 | Valid: valid, 46 | }, nil 47 | } 48 | -------------------------------------------------------------------------------- /server/dto/modules/department/department.go: -------------------------------------------------------------------------------- 1 | package departmentResponsiesModules 2 | 3 | type CreateDepartmentRequest struct { 4 | DepartmentName string `form:"departmentName" binding:"required"` 5 | ParentDepartment *string `form:"parentDepartment"` 6 | DepartmentDescription string `form:"departmentDescription"` 7 | DepartmentOrder int `form:"departmentOrder"` 8 | } 9 | 10 | type DepartmentResponse struct { 11 | ID string `json:"id"` 12 | DepartmentName string `json:"departmentName"` 13 | ParentDepartment *string `json:"parentDepartment"` 14 | DepartmentOrder int `json:"departmentOrder"` 15 | DepartmentDescription *string `json:"departmentDescription"` 16 | Children []*DepartmentResponse `json:"children"` 17 | CreateTime string `json:"createTime"` 18 | UpdateTime string `json:"updateTime"` 19 | } 20 | 21 | type UpdateDepartmentRequest struct { 22 | DepartmentName string `form:"departmentName"` 23 | ParentDepartment string `form:"parentDepartment"` 24 | DepartmentDescription string `json:"departmentDescription"` 25 | DepartmentOrder int `form:"departmentOrder"` 26 | } 27 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/deleterolelogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | roleModlel "micro/model/role" 7 | 8 | "micro/rpc/role/internal/svc" 9 | "micro/rpc/role/roleRPC" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type DeleteRoleLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewDeleteRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteRoleLogic { 21 | return &DeleteRoleLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | func (l *DeleteRoleLogic) DeleteRole(in *roleRPC.DeleteRoleRequest) (*roleRPC.CommonResponse, error) { 29 | existLogic := NewRoleIDsHasBeenExistLogic(l.ctx, l.svcCtx) 30 | exist, err := existLogic.RoleIDsHasBeenExist(&roleRPC.RoleIDsHasBeenExistRequest{Ids: []string{in.Id}}) 31 | 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | if !exist.Ok { 37 | return nil, errors.New("角色ID不存在") 38 | } 39 | 40 | if err := l.svcCtx.GormDB.Delete(&roleModlel.Role{}, "id = ?", in.Id).Error; err != nil { 41 | return &roleRPC.CommonResponse{ 42 | Ok: false, 43 | Msg: err.Error(), 44 | }, nil 45 | } 46 | return &roleRPC.CommonResponse{ 47 | Ok: true, 48 | Msg: "删除成功", 49 | }, nil 50 | } 51 | -------------------------------------------------------------------------------- /web/src/components/Translate/translate.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from 'react'; 2 | import TranslateDark from '@/components/Icon/translateDark.tsx'; 3 | import { useTheme } from '@/hooks/useTheme.ts'; 4 | import TranslateLight from '@/components/Icon/translateLight.tsx'; 5 | import { Dropdown, MenuProps } from 'antd'; 6 | import { useTranslation } from 'react-i18next'; 7 | import { changeLang } from '@/store/UIStore'; 8 | import { useDispatch } from 'react-redux'; 9 | 10 | const Translate: FC = () => { 11 | const { themeMode } = useTheme(); 12 | const { i18n } = useTranslation(); 13 | const dispatch = useDispatch(); 14 | const items: MenuProps['items'] = [ 15 | { 16 | key: 'zhCN', 17 | label: '简体中文', 18 | }, 19 | { 20 | key: 'enUS', 21 | label: 'English', 22 | }, 23 | ]; 24 | const onClick: MenuProps['onClick'] = async ({ key }) => { 25 | dispatch(changeLang(key as 'zhCN' | 'enUS')); 26 | await i18n.changeLanguage(key === 'zhCN' ? 'zh' : 'en'); 27 | }; 28 | 29 | return ( 30 |
31 | 32 |
{themeMode === 'light' ? : }
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default memo(Translate); 39 | -------------------------------------------------------------------------------- /web/src/store/UserStore/index.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import { cache } from '@/utils'; 3 | import { constants } from '@/constant'; 4 | import { menuType } from '@/types/menus'; 5 | import { LoginUserResponseType } from '@/service'; 6 | 7 | interface IUserStore { 8 | token: string | null; 9 | userInfo: LoginUserResponseType | undefined; 10 | menus: menuType[]; 11 | userInterfaceDic: string[]; 12 | } 13 | 14 | const UserStoreSlice = createSlice({ 15 | name: 'UserStore', 16 | initialState: { 17 | token: null, 18 | userInfo: undefined, 19 | menus: [], 20 | userInterfaceDic: [], 21 | } as IUserStore, 22 | reducers: { 23 | changeToken(state, action) { 24 | cache.set(constants.localStorage.token, action.payload); 25 | state.token = action.payload; 26 | }, 27 | changeUserInfo(state, action) { 28 | state.userInfo = action.payload; 29 | }, 30 | changeMenus(state, action) { 31 | state.menus = action.payload; 32 | }, 33 | changeAllInterfaceDic(state, action: { payload: string[]; type: string }) { 34 | state.userInterfaceDic = action.payload; 35 | }, 36 | }, 37 | }); 38 | 39 | export default UserStoreSlice.reducer; 40 | export const { changeToken, changeUserInfo, changeMenus, changeAllInterfaceDic } = UserStoreSlice.actions; 41 | -------------------------------------------------------------------------------- /web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import viteCompression from 'vite-plugin-compression'; 4 | 5 | const prefix = `monaco-editor/esm/vs`; 6 | 7 | export default defineConfig({ 8 | plugins: [react()], 9 | resolve: { 10 | alias: { 11 | '@': '/src', 12 | }, 13 | }, 14 | build: { 15 | rollupOptions: { 16 | output: { 17 | manualChunks: { 18 | tsWorker: [`${prefix}/language/typescript/ts.worker`], 19 | editorWorker: [`${prefix}/editor/editor.worker`], 20 | }, 21 | chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称 22 | entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称 23 | assetFileNames: '[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等 24 | }, 25 | plugins: [viteCompression()], 26 | }, 27 | target: ['esnext'], 28 | terserOptions: { 29 | enclose: false, 30 | compress: true, 31 | sourceMap: false, 32 | }, 33 | }, 34 | server: { 35 | proxy: { 36 | '/cms': { 37 | target: 'http://localhost:8081', 38 | changeOrigin: true, 39 | }, 40 | }, 41 | }, 42 | preview: { 43 | proxy: { 44 | '/cms': { 45 | target: 'http://127.124.28.77:8081', 46 | changeOrigin: true, 47 | }, 48 | }, 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/getuserbyaccountlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | userModel "micro/model/user" 7 | 8 | "micro/rpc/user/internal/svc" 9 | "micro/rpc/user/userRPC" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type GetUserByAccountLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewGetUserByAccountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserByAccountLogic { 21 | return &GetUserByAccountLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | func (l *GetUserByAccountLogic) GetUserByAccount(in *userRPC.GetUserByAccountRequest) (*userRPC.GetUserResponse, error) { 29 | var user *userModel.User 30 | if err := l.svcCtx.GormDB.Model(&userModel.User{}).First(&user, "account = ?", in.Account).Error; err != nil { 31 | fmt.Println(err) 32 | return nil, err 33 | } 34 | return &userRPC.GetUserResponse{ 35 | Id: user.ID, 36 | Account: user.Account, 37 | Password: "", 38 | Nickname: user.NickName, 39 | Avatar: user.Avatar, 40 | Status: user.Status, 41 | Department: user.DepartmentID, 42 | IsAdmin: user.IsAdmin, 43 | CreateTime: user.CreatedAt.String(), 44 | UpdateTime: user.UpdatedAt.String(), 45 | }, nil 46 | } 47 | -------------------------------------------------------------------------------- /web/src/service/api/department/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export interface IDepartmentResponse { 5 | id: string; 6 | departmentName: string; 7 | parentDepartment: string; 8 | departmentOrder: number; 9 | departmentDescription: string; 10 | children: IDepartmentResponse[]; 11 | createTime: string; 12 | updateTime: string; 13 | } 14 | 15 | export interface IAddDepartmentRequest { 16 | departmentName: string; 17 | parentDepartment: string; 18 | departmentOrder: number; 19 | departmentDescription: string; 20 | } 21 | 22 | export const getDepartmentRequest = () => { 23 | return request.get>({ 24 | url: '/department', 25 | }); 26 | }; 27 | 28 | export const addDepartmentRequest = (data: IAddDepartmentRequest) => { 29 | return request.post>({ 30 | url: '/department', 31 | data, 32 | }); 33 | }; 34 | 35 | export const deleteDepartmentRequest = (id: string) => { 36 | return request.delete>({ 37 | url: `/department/${id}`, 38 | }); 39 | }; 40 | 41 | export const updateDepartmentRequest = (id: string, data: IAddDepartmentRequest) => { 42 | return request.patch>({ 43 | url: `/department/${id}`, 44 | data, 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /micro/api/user/internal/logic/createuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "micro/rpc/user/userRPC" 6 | "micro/shared/snowflake" 7 | "strconv" 8 | 9 | "micro/api/user/internal/svc" 10 | "micro/api/user/internal/types" 11 | 12 | "github.com/zeromicro/go-zero/core/logx" 13 | ) 14 | 15 | type CreateUserLogic struct { 16 | logx.Logger 17 | ctx context.Context 18 | svcCtx *svc.ServiceContext 19 | } 20 | 21 | func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic { 22 | return &CreateUserLogic{ 23 | Logger: logx.WithContext(ctx), 24 | ctx: ctx, 25 | svcCtx: svcCtx, 26 | } 27 | } 28 | 29 | func (l *CreateUserLogic) CreateUser(req *types.CreateUserRequest) (resp *types.UpdateUserResponse, err error) { 30 | response, _ := l.svcCtx.UserService.CreateUser(l.ctx, &userRPC.CreateUserRequest{ 31 | Id: strconv.FormatInt(snowflake.GenID(), 10), 32 | Account: req.Account, 33 | Password: req.Password, 34 | Nickname: req.Nickname, 35 | Avatar: req.Avatar, 36 | Status: req.Status, 37 | Department: req.DepartmentID, 38 | IsAdmin: req.IsAdmin, 39 | }) 40 | if response.Ok { 41 | return &types.UpdateUserResponse{ 42 | Code: 200, 43 | Msg: response.Msg, 44 | }, nil 45 | } 46 | return &types.UpdateUserResponse{ 47 | Code: 500, 48 | Msg: response.Msg, 49 | }, nil 50 | } 51 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/updaterolelogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/zeromicro/go-zero/core/logx" 7 | roleModlel "micro/model/role" 8 | "micro/rpc/role/internal/svc" 9 | "micro/rpc/role/roleRPC" 10 | ) 11 | 12 | type UpdateRoleLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewUpdateRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateRoleLogic { 19 | return &UpdateRoleLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | func (l *UpdateRoleLogic) UpdateRole(in *roleRPC.UpdateRoleRequest) (*roleRPC.CommonResponse, error) { 27 | existLogic := NewRoleIDsHasBeenExistLogic(l.ctx, l.svcCtx) 28 | exist, err := existLogic.RoleIDsHasBeenExist(&roleRPC.RoleIDsHasBeenExistRequest{Ids: []string{in.Id}}) 29 | 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | if !exist.Ok { 35 | return nil, errors.New("角色ID不存在") 36 | } 37 | 38 | if err = l.svcCtx.GormDB.Model(&roleModlel.Role{}).Where("id = ?", in.Id).Updates(&roleModlel.Role{ 39 | RoleName: in.RoleName, 40 | Description: in.Description, 41 | CanEdit: in.CanEdit, 42 | }).Error; err != nil { 43 | return nil, err 44 | } 45 | 46 | return &roleRPC.CommonResponse{ 47 | Ok: true, 48 | Msg: "更新成功", 49 | }, nil 50 | } 51 | -------------------------------------------------------------------------------- /server/utils/modules/translator/translator.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin/binding" 6 | "github.com/go-playground/locales/en" 7 | "github.com/go-playground/locales/zh" 8 | ut "github.com/go-playground/universal-translator" 9 | "github.com/go-playground/validator/v10" 10 | enTranslations "github.com/go-playground/validator/v10/translations/en" 11 | zhTranslations "github.com/go-playground/validator/v10/translations/zh" 12 | ) 13 | 14 | var Trans ut.Translator // 全局验证器 15 | 16 | func ValidatorTrans(locale string) (err error) { 17 | // 修改gin框架中的Validator引擎属性,实现自定制 18 | if v, ok := binding.Validator.Engine().(*validator.Validate); ok { 19 | 20 | zhT := zh.New() // 中文翻译器 21 | enT := en.New() // 英文翻译器 22 | uni := ut.New(enT, zhT, enT) 23 | 24 | // locale 通常取决于 http 请求头的 'Accept-Language' 25 | var ok bool 26 | // 也可以使用 uni.FindTranslator(...) 传入多个locale进行查找 27 | Trans, ok = uni.GetTranslator(locale) 28 | if !ok { 29 | return fmt.Errorf("uni.GetTranslator(%s) failed", locale) 30 | } 31 | 32 | // 注册翻译器 33 | switch locale { 34 | case "en": 35 | err = enTranslations.RegisterDefaultTranslations(v, Trans) 36 | case "zh": 37 | err = zhTranslations.RegisterDefaultTranslations(v, Trans) 38 | default: 39 | err = enTranslations.RegisterDefaultTranslations(v, Trans) 40 | } 41 | return nil 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /web/src/styles/common/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .physicLight { 6 | border-radius: 20px; 7 | background: #ededed; 8 | box-shadow: 9px 9px 18px #c9c9c9, 9 | -9px -9px 18px #ffffff; 10 | } 11 | 12 | .physicLightCard { 13 | border-radius: 10px; 14 | background: #fff; 15 | box-shadow: 9px 9px 18px #c9c9c9, 16 | -9px -9px 18px #ececec; 17 | } 18 | 19 | .physicDark { 20 | border-radius: 20px; 21 | background: #142433; 22 | box-shadow: 9px 9px 18px #272727, 23 | -9px -9px 18px #353535; 24 | } 25 | 26 | .physicDarkDashBoard { 27 | border-radius: 20px; 28 | background: #2e2e2e; 29 | box-shadow: 9px 9px 18px #000000; 30 | } 31 | 32 | .physicDarkCard { 33 | border-radius: 10px; 34 | background: #001624; 35 | box-shadow: 9px 9px 18px #272727, 36 | -9px -9px 18px #2d2d2d; 37 | } 38 | 39 | @layer utilities { 40 | .tran { 41 | transition: all 0.2s ease; 42 | } 43 | 44 | * { 45 | /* Hide scrollbar for Chrome, Safari and Opera */ 46 | 47 | .no-scrollbar::-webkit-scrollbar { 48 | display: none; 49 | } 50 | 51 | /* Hide scrollbar for IE, Edge and Firefox */ 52 | 53 | .no-scrollbar { 54 | -ms-overflow-style: none; /* IE and Edge */ 55 | scrollbar-width: none; /* Firefox */ 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/deleteuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | "micro/rpc/user/internal/svc" 7 | "micro/rpc/user/userRPC" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type DeleteUserLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewDeleteUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteUserLogic { 19 | return &DeleteUserLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | func (l *DeleteUserLogic) DeleteUser(in *userRPC.DeleteUserRequest) (*userRPC.CommonResponse, error) { 27 | existLogic := NewUserIDHasBeenExistLogic(l.ctx, l.svcCtx) 28 | exist, err := existLogic.UserIDHasBeenExist(&userRPC.DeleteUserRequest{Id: in.Id}) 29 | if err != nil { 30 | return &userRPC.CommonResponse{ 31 | Ok: false, 32 | Msg: err.Error(), 33 | }, err 34 | } 35 | if !exist.Ok { 36 | return &userRPC.CommonResponse{ 37 | Ok: false, 38 | Msg: "用户不存在", 39 | }, nil 40 | } 41 | if err := l.svcCtx.GormDB.Where("id = ?", in.Id).Unscoped().Delete(&userModel.User{}).Error; err != nil { 42 | return &userRPC.CommonResponse{ 43 | Ok: false, 44 | Msg: err.Error(), 45 | }, nil 46 | } 47 | return &userRPC.CommonResponse{ 48 | Ok: true, 49 | Msg: "删除成功", 50 | }, nil 51 | } 52 | -------------------------------------------------------------------------------- /micro/api/auth/internal/logic/captchalogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "micro/rpc/captcha/captcha" 6 | "micro/shared/snowflake" 7 | "strconv" 8 | 9 | "micro/api/auth/internal/svc" 10 | "micro/api/auth/internal/types" 11 | 12 | "github.com/zeromicro/go-zero/core/logx" 13 | ) 14 | 15 | type CaptchaLogic struct { 16 | logx.Logger 17 | ctx context.Context 18 | svcCtx *svc.ServiceContext 19 | } 20 | 21 | func NewCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CaptchaLogic { 22 | return &CaptchaLogic{ 23 | Logger: logx.WithContext(ctx), 24 | ctx: ctx, 25 | svcCtx: svcCtx, 26 | } 27 | } 28 | 29 | func (l *CaptchaLogic) Captcha(req *types.EmptyRequest) (resp *types.CommonResponse, err error) { 30 | sessionID := strconv.FormatInt(snowflake.GenID(), 10) 31 | generateCaptcha, err := l.svcCtx.CaptchaService.GenerateCaptcha(l.ctx, &captcha.GenerateCaptchaRequest{ 32 | SessionId: sessionID, 33 | }) 34 | 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return &types.CommonResponse{ 40 | Code: 0, 41 | Data: &struct { 42 | Captcha string `json:"captchaCode"` 43 | SessionID string `json:"sessionID"` 44 | CaptchaCode string `json:"captchaBase64Code"` 45 | }{ 46 | CaptchaCode: generateCaptcha.CaptchaCode, 47 | Captcha: generateCaptcha.Captcha, 48 | SessionID: sessionID, 49 | }, 50 | Msg: "success", 51 | }, nil 52 | } 53 | -------------------------------------------------------------------------------- /web/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers, configureStore } from '@reduxjs/toolkit'; 2 | import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; 3 | import UseUIStoreSlice from '@/store/UIStore'; 4 | import UserStoreSlice from '@/store/UserStore'; 5 | import useUploadStore from '@/store/UploadStore'; 6 | import storage from 'redux-persist/es/storage'; 7 | import { useDispatch, useSelector } from 'react-redux'; 8 | 9 | const reducers = combineReducers({ 10 | UIStore: UseUIStoreSlice, 11 | UserStore: UserStoreSlice, 12 | UploadStore: useUploadStore, 13 | }); 14 | 15 | const persistedReducer = persistReducer( 16 | { 17 | key: 'cms', 18 | version: 1, 19 | storage, 20 | whitelist: ['UIStore', 'UserStore'], 21 | blacklist: ['UploadStore'], 22 | }, 23 | reducers, 24 | ); 25 | 26 | const store = configureStore({ 27 | reducer: persistedReducer, 28 | devTools: import.meta.env.NODE_ENV !== 'production', 29 | middleware: (getDefaultMiddleware) => 30 | getDefaultMiddleware({ 31 | serializableCheck: { 32 | ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], 33 | }, 34 | }), 35 | }); 36 | 37 | export type RootState = ReturnType; 38 | export const useAppSelector = (selector: (state: RootState) => T) => useSelector(selector); 39 | export const useAppDispatch = useDispatch; 40 | export default store; 41 | -------------------------------------------------------------------------------- /web/src/service/request/index.ts: -------------------------------------------------------------------------------- 1 | import Request from '@/service/request/lib'; 2 | import { IResponse } from '@/service/request/lib/type.ts'; 3 | import { cache, message } from '@/utils'; 4 | import { constants } from '@/constant'; 5 | 6 | const request = new Request(import.meta.env.VITE_APP_BASE_URL, 1000 * 60, { 7 | requestInterceptor: { 8 | onFulfilled(config) { 9 | const token = cache.get(constants.localStorage.token); 10 | if (token) { 11 | config.headers.Authorization = token; 12 | } 13 | return config; 14 | }, 15 | onRejected(error) { 16 | return error; 17 | }, 18 | }, 19 | responseInterceptor: { 20 | onFulfilled: (value) => { 21 | if (value.data.code > 201) { 22 | message.error(value.data.msg); 23 | } 24 | if (value.data.code == 401) { 25 | cache.clear(); 26 | window.location.replace(constants.routePath.login); 27 | message.error('登录过期,请重新登录'); 28 | return; 29 | } 30 | return value.data; 31 | }, 32 | onRejected(error) { 33 | const { msg } = error.response.data; 34 | message.error(msg); 35 | if (error.response.data.code == 401) { 36 | cache.clear(); 37 | window.location.replace(constants.routePath.login); 38 | message.error('登录过期,请重新登录'); 39 | return; 40 | } 41 | return error; 42 | }, 43 | }, 44 | }); 45 | 46 | export default request; 47 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/getrolelogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | roleModlel "micro/model/role" 7 | 8 | "micro/rpc/role/internal/svc" 9 | "micro/rpc/role/roleRPC" 10 | 11 | "github.com/zeromicro/go-zero/core/logx" 12 | ) 13 | 14 | type GetRoleLogic struct { 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | logx.Logger 18 | } 19 | 20 | func NewGetRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRoleLogic { 21 | return &GetRoleLogic{ 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | Logger: logx.WithContext(ctx), 25 | } 26 | } 27 | 28 | func (l *GetRoleLogic) GetRole(in *roleRPC.GetRoleRequest) (*roleRPC.GetRoleResponse, error) { 29 | existLogic := NewRoleIDsHasBeenExistLogic(l.ctx, l.svcCtx) 30 | exist, err := existLogic.RoleIDsHasBeenExist(&roleRPC.RoleIDsHasBeenExistRequest{Ids: []string{in.Id}}) 31 | 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | if !exist.Ok { 37 | return nil, errors.New("角色ID不存在") 38 | } 39 | 40 | role := roleModlel.Role{} 41 | err = l.svcCtx.GormDB.Model(&roleModlel.Role{}).First(&role, in.Id).Error 42 | if err != nil { 43 | return nil, err 44 | } 45 | return &roleRPC.GetRoleResponse{ 46 | Id: role.ID, 47 | RoleName: role.RoleName, 48 | Description: role.Description, 49 | CanEdit: role.CanEdit, 50 | CreateTime: role.CreatedAt.String(), 51 | UpdateTime: role.UpdatedAt.String(), 52 | }, nil 53 | } 54 | -------------------------------------------------------------------------------- /server/services/modules/department/department.go: -------------------------------------------------------------------------------- 1 | package departmentServiceModules 2 | 3 | import ( 4 | "errors" 5 | "github.com/Xi-Yuer/cms/dto" 6 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 7 | "github.com/Xi-Yuer/cms/utils" 8 | ) 9 | 10 | var DepartmentService = &departmentService{} 11 | 12 | type departmentService struct{} 13 | 14 | func (d *departmentService) CreateDepartment(params *dto.CreateDepartmentRequest) error { 15 | return repositories.DepartmentRepository.CreateDepartment(params) 16 | } 17 | 18 | func (d *departmentService) DeleteDepartment(id string) error { 19 | if department := repositories.DepartmentRepository.GetDepartmentByID(id); department.ID == "" { 20 | return errors.New("资源不存在") 21 | } 22 | return repositories.DepartmentRepository.DeleteDepartment(id) 23 | } 24 | 25 | func (d *departmentService) GetDepartments() ([]*dto.DepartmentResponse, error) { 26 | department, err := repositories.DepartmentRepository.GetDepartments() 27 | if err != nil { 28 | return nil, err 29 | } 30 | buildDepartment := utils.BuildDepartment(department) 31 | return buildDepartment, nil 32 | } 33 | 34 | func (d *departmentService) UpdateDepartment(id string, params *dto.UpdateDepartmentRequest) error { 35 | if department := repositories.DepartmentRepository.GetDepartmentByID(id); department.ID == "" { 36 | return errors.New("资源不存在") 37 | } 38 | return repositories.DepartmentRepository.UpdateDepartment(id, params) 39 | } 40 | -------------------------------------------------------------------------------- /micro/rpc/captcha/internal/logic/generatecaptchalogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "math/rand" 7 | "micro/shared/image" 8 | "time" 9 | 10 | "micro/rpc/captcha/captcha" 11 | "micro/rpc/captcha/internal/svc" 12 | 13 | "github.com/zeromicro/go-zero/core/logx" 14 | ) 15 | 16 | type GenerateCaptchaLogic struct { 17 | ctx context.Context 18 | svcCtx *svc.ServiceContext 19 | logx.Logger 20 | } 21 | 22 | func NewGenerateCaptchaLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateCaptchaLogic { 23 | return &GenerateCaptchaLogic{ 24 | ctx: ctx, 25 | svcCtx: svcCtx, 26 | Logger: logx.WithContext(ctx), 27 | } 28 | } 29 | 30 | func (l *GenerateCaptchaLogic) GenerateCaptcha(in *captcha.GenerateCaptchaRequest) (*captcha.GenerateCaptchaResponse, error) { 31 | sessionID := in.GetSessionId() 32 | 33 | // 生成一个随机的 6 位验证码 34 | code := fmt.Sprintf("%06d", rand.Intn(1000000)) 35 | 36 | // 设置验证码的过期时间为 5 分钟后 37 | expiresAt := time.Now().Add(5 * time.Minute).Unix() 38 | 39 | // 将验证码存储到 Redis 中,以 sessionID 为键 40 | if err := l.svcCtx.Redis.Set(l.ctx, fmt.Sprintf("captcha:%s", sessionID), code, 5*time.Minute).Err(); err != nil { 41 | return nil, err 42 | } 43 | 44 | // 生成验证码图片 45 | captchaImage, err := image.GenerateCaptchaImage(code) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return &captcha.GenerateCaptchaResponse{ 50 | CaptchaCode: captchaImage, 51 | Captcha: code, 52 | ExpiresAt: expiresAt, 53 | }, nil 54 | } 55 | -------------------------------------------------------------------------------- /server/controllers/modules/commits/commits.go: -------------------------------------------------------------------------------- 1 | package commitsControllerModules 2 | 3 | import ( 4 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 5 | "github.com/Xi-Yuer/cms/utils" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | var CommitsController = &commitsController{} 10 | 11 | type commitsController struct{} 12 | 13 | // GetCommits 获取Git提交记录 14 | // @Summary 获取Git提交记录 15 | // @Description 获取Git提交记录 16 | // @Tags 日志管理 17 | // @Accept json 18 | // @Produce json 19 | // @Success 200 {string} json "{"code":200,"data":{},"msg":"ok"}" 20 | // @Router /log/commits [get] 21 | func (c *commitsController) GetCommits(context *gin.Context) { 22 | commits := repositories.CommitsRepositoryModules.GetCommits() 23 | // 分类提交记录到日期 24 | groupedCommits := utils.GroupCommitsByDate(commits) 25 | 26 | // 格式化为所需的结构 27 | formattedCommits := utils.FormatCommits(groupedCommits) 28 | utils.Response.Success(context, formattedCommits) 29 | } 30 | 31 | // GetCommitsCount 获取Git提交次数 32 | // @Summary 获取Git提交次数 33 | // @Description 获取Git提交次数 34 | // @Tags 日志管理 35 | // @Accept json 36 | // @Produce json 37 | // @Success 200 {string} json "{"code":200,"data":{},"msg":"ok"}" 38 | // @Router /log/commits/count [get] 39 | func (c *commitsController) GetCommitsCount(context *gin.Context) { 40 | count, err := repositories.CommitsRepositoryModules.GetCommitsCount() 41 | if err != nil { 42 | utils.Response.ServerError(context, err.Error()) 43 | return 44 | } 45 | utils.Response.Success(context, count) 46 | } 47 | -------------------------------------------------------------------------------- /micro/shared/token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "fmt" 5 | "github.com/golang-jwt/jwt/v4" 6 | "time" 7 | ) 8 | 9 | // 定义一个密钥,用于签名和验证 Token 10 | var jwtKey = []byte("12345699999") 11 | 12 | // Claims 结构定义了 Token 中的声明 13 | type Claims struct { 14 | UserID string `json:"user_id"` 15 | UserAccount string `json:"user_account"` 16 | jwt.StandardClaims 17 | } 18 | 19 | // CreateToken 创建 Token 20 | func CreateToken(userID string, userAccount string) (string, error) { 21 | // 设置 Token 的过期时间 22 | expirationTime := time.Now().Add(24 * 60 * time.Minute) 23 | 24 | // 创建声明 25 | claims := &Claims{ 26 | UserID: userID, 27 | UserAccount: userAccount, 28 | StandardClaims: jwt.StandardClaims{ 29 | ExpiresAt: expirationTime.Unix(), 30 | }, 31 | } 32 | 33 | // 创建 Token 对象并签名 34 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 35 | signedToken, err := token.SignedString(jwtKey) 36 | if err != nil { 37 | return "", err 38 | } 39 | 40 | return signedToken, nil 41 | } 42 | 43 | // VerifyToken 验证 44 | func VerifyToken(tokenString string) (*Claims, error) { 45 | // 解析 Token 46 | token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { 47 | return jwtKey, nil 48 | }) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | // 检查 Token 的有效性 54 | if claims, ok := token.Claims.(*Claims); ok && token.Valid { 55 | return claims, nil 56 | } else { 57 | return nil, fmt.Errorf("invalid token") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /server/template/server/service/service.go: -------------------------------------------------------------------------------- 1 | package TableNameService 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/template/server/dto" 5 | "github.com/Xi-Yuer/cms/template/server/repository" 6 | ) 7 | 8 | type ServiceInterface interface { 9 | GetTableNameListService(params *TableNameDto.TableNameFindRequestDTO) ([]*TableNameDto.TableNameFindRequestDTO, error) 10 | CreateTableNameRecordService(params *TableNameDto.TableNameCreateRequestDTO) error 11 | UpdateTableNameListService(id string, params *TableNameDto.TableNameUpdateRequestDTO) error 12 | DeleteTableNameRecordService(id string) error 13 | } 14 | type Service struct { 15 | repo TableNameRepository.RepositoryInterface 16 | } 17 | 18 | func NewTableNameService() *Service { 19 | return &Service{ 20 | repo: TableNameRepository.NewTableNameRepository(), 21 | } 22 | } 23 | 24 | func (s Service) GetTableNameListService(params *TableNameDto.TableNameFindRequestDTO) ([]*TableNameDto.TableNameFindRequestDTO, error) { 25 | return s.repo.GetTableNameListRepo(params) 26 | } 27 | 28 | func (s Service) CreateTableNameRecordService(params *TableNameDto.TableNameCreateRequestDTO) error { 29 | return s.repo.CreateTableNameRecordRepo(params) 30 | } 31 | 32 | func (s Service) UpdateTableNameListService(id string, params *TableNameDto.TableNameUpdateRequestDTO) error { 33 | return s.repo.UpdateTableNameRecordRepo(id, params) 34 | } 35 | 36 | func (s Service) DeleteTableNameRecordService(id string) error { 37 | return s.repo.DeleteTableNameRecordRepo(id) 38 | } 39 | -------------------------------------------------------------------------------- /web/src/service/api/interface/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export interface IInterfaceResponse { 5 | id: string; 6 | interfaceName: string; 7 | interfaceMethod: string; 8 | interfacePageID: string; 9 | interfacePath: string; 10 | interfaceDesc: string; 11 | interfaceDic: string; 12 | createTime: string; 13 | updateTime: string; 14 | } 15 | 16 | export interface IAllPageInterfaceListResponse { 17 | key: string; 18 | children: IInterfaceResponse[]; 19 | } 20 | 21 | // 获取页面接口 22 | export const getInterfaceListByPageIDRequest = (id: string) => { 23 | return request.get>({ 24 | url: `/interface/page/${id}`, 25 | }); 26 | }; 27 | 28 | // 按照页面分类获取所有接口 29 | export const getInterfaceAllListRequest = () => { 30 | return request.get>({ 31 | url: `/interface`, 32 | }); 33 | }; 34 | 35 | export const deleteInterfaceRequest = (id: string) => { 36 | return request.delete>({ 37 | url: `/interface/${id}`, 38 | }); 39 | }; 40 | 41 | export const addInterfaceRequest = (data: IInterfaceResponse) => { 42 | return request.post>({ 43 | url: `/interface`, 44 | data, 45 | }); 46 | }; 47 | 48 | export const updateInterfaceRequest = (data: IInterfaceResponse) => { 49 | return request.patch>({ 50 | url: `/interface/${data.id}`, 51 | data, 52 | }); 53 | }; 54 | -------------------------------------------------------------------------------- /web/src/hooks/useAppRoutes.tsx: -------------------------------------------------------------------------------- 1 | import { RouteObject, useLocation, useNavigate, useRoutes } from 'react-router-dom'; 2 | import { useAppSelector } from '@/store'; 3 | import { builderMenuRoutes, getFirstMenu } from '@/utils'; 4 | import { useEffect, useState } from 'react'; 5 | import routes from '@/router'; 6 | import NotFond from '@/pages/NotFond'; 7 | import { constants } from '@/constant'; 8 | 9 | export const useAppRouter = () => { 10 | const navigate = useNavigate(); 11 | const { menus, token } = useAppSelector((state) => state.UserStore); 12 | const { pathname } = useLocation(); 13 | const [routesWithMenus, setRoutesWithMenus] = useState([]); 14 | 15 | useEffect(() => { 16 | if (!token) { 17 | navigate(constants.routePath.login, { replace: true }); 18 | } 19 | }, [token, navigate]); 20 | 21 | useEffect(() => { 22 | const routesWithDynamicMenus = [...routes]; // 假设 routes 是一个外部定义的数组 23 | routesWithDynamicMenus[1].children = builderMenuRoutes(menus); 24 | routesWithDynamicMenus[1].children?.push({ 25 | path: '*', 26 | element: , 27 | }); 28 | 29 | setRoutesWithMenus(routesWithDynamicMenus); 30 | if (pathname === constants.routePath.main) { 31 | if (!menus.length) return; 32 | const firstMenuPath = getFirstMenu(menus)?.pagePath || constants.routePath.login; 33 | navigate(firstMenuPath); 34 | } 35 | }, [menus, pathname, navigate]); 36 | 37 | return { 38 | element: useRoutes(routesWithMenus), 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /server/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/utils/modules/bcrypt" 5 | "github.com/Xi-Yuer/cms/utils/modules/buildTree" 6 | "github.com/Xi-Yuer/cms/utils/modules/buildZip" 7 | "github.com/Xi-Yuer/cms/utils/modules/captcha" 8 | "github.com/Xi-Yuer/cms/utils/modules/contain" 9 | "github.com/Xi-Yuer/cms/utils/modules/exportExcel" 10 | "github.com/Xi-Yuer/cms/utils/modules/file" 11 | "github.com/Xi-Yuer/cms/utils/modules/jwt" 12 | "github.com/Xi-Yuer/cms/utils/modules/logs" 13 | "github.com/Xi-Yuer/cms/utils/modules/response" 14 | "github.com/Xi-Yuer/cms/utils/modules/snowflake" 15 | "github.com/Xi-Yuer/cms/utils/modules/timeTask" 16 | "github.com/Xi-Yuer/cms/utils/modules/translator" 17 | "github.com/Xi-Yuer/cms/utils/modules/unique" 18 | ) 19 | 20 | var Log = logsModules.Log 21 | var Response = responseModules.Response 22 | var GenID = snowflake.GenID 23 | var Bcrypt = &bcrypt.Bcrypt{} 24 | var Translator = translator.ValidatorTrans 25 | var Trans = translator.Trans 26 | var Captcha = &captcha.Captcha{} 27 | var Jsonwebtoken = &jwt.Jsonwebtoken{} 28 | var Unique = unique.RemoveDuplicatesAndEmpty 29 | var BuildPages = buildTree.BuildMenu 30 | var BuildDepartment = buildTree.BuildDepartment 31 | var Contain = contain.StringInSlice 32 | var ExportExcel = exportExcel.ExportExcel 33 | var TimeTask = timeTask.TimeTask 34 | var File = file.File 35 | var FormatCommits = buildTree.FormatCommits 36 | var GroupCommitsByDate = buildTree.GroupCommitsByDate 37 | var CreateFilesAndZip = buildZip.CreateFilesAndZip 38 | -------------------------------------------------------------------------------- /server/services/modules/interface/interface.go: -------------------------------------------------------------------------------- 1 | package interfaceServiceModules 2 | 3 | import ( 4 | "errors" 5 | "github.com/Xi-Yuer/cms/dto" 6 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 7 | ) 8 | 9 | var InterfaceService = &interfaceService{} 10 | 11 | type interfaceService struct{} 12 | 13 | func (i *interfaceService) CreateInterface(params *dto.CreateInterfaceRequest) error { 14 | return repositories.InterfaceRepository.CreateInterface(params) 15 | } 16 | 17 | func (i *interfaceService) GetInterfaceByPageID(id string) []*dto.GetInterfaceResponse { 18 | return repositories.InterfaceRepository.GetInterfaceByPageID(id) 19 | } 20 | 21 | func (i *interfaceService) UpdateInterfaceByID(id string, params *dto.UpdateInterfaceRequest) error { 22 | inter, exist := repositories.InterfaceRepository.GetInterfaceByID(id) 23 | if !exist { 24 | return errors.New("资源不存在") 25 | } 26 | if !inter.CanEdit { 27 | return errors.New("该资源无法修改") 28 | } 29 | return repositories.InterfaceRepository.UpdateInterfaceByID(id, params) 30 | } 31 | 32 | func (i *interfaceService) DeleteInterfaceByID(id string) error { 33 | inter, exist := repositories.InterfaceRepository.GetInterfaceByID(id) 34 | if !exist { 35 | return errors.New("资源不存在") 36 | } 37 | if !inter.CanEdit { 38 | return errors.New("该资源无法删除") 39 | } 40 | return repositories.InterfaceRepository.DeleteInterfaceByID(id) 41 | } 42 | 43 | func (i *interfaceService) GetAllInterface() ([]*dto.AllInterfaceResponse, error) { 44 | return repositories.InterfaceRepository.GetAllInterface() 45 | } 46 | -------------------------------------------------------------------------------- /server/middlewares/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package systemLogsMiddlewareModule 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/constant" 5 | "github.com/Xi-Yuer/cms/dto" 6 | repositories "github.com/Xi-Yuer/cms/repositories/modules" 7 | "github.com/Xi-Yuer/cms/utils" 8 | "github.com/gin-gonic/gin" 9 | "time" 10 | ) 11 | 12 | var SystemLogMiddlewareModule = &systemLogMiddlewareModule{} 13 | 14 | type systemLogMiddlewareModule struct{} 15 | 16 | // SystemLogMiddleware 系统日志中间件 17 | // 用于记录系统日志的中间件 18 | // 例如:记录请求的URL、请求的方法、请求的参数、请求的IP地址等 19 | // 还可以记录响应的状态码、响应的时间等 20 | // 可以根据需要记录更多的信息 21 | // 该中间件可以用于记录系统日志,以便进行问题排查和分析 22 | func (m *systemLogMiddlewareModule) SystemLogMiddleware(context *gin.Context) { 23 | path := context.Request.URL.Path 24 | methods := context.Request.Method 25 | status := context.Writer.Status() 26 | ip := context.ClientIP() 27 | start := time.Now() 28 | context.Next() 29 | duration := time.Since(start) 30 | user := &dto.JWTPayload{} 31 | value, exists := context.Get(constant.JWTPAYLOAD) 32 | if !exists { 33 | user.Account = "未登录用户" 34 | user.ID = "" 35 | } else { 36 | user = value.(*dto.JWTPayload) 37 | } 38 | params := dto.CreateLogRecordRequest{ 39 | ID: utils.GenID(), 40 | UserName: user.Account, 41 | UserID: user.ID, 42 | UserIP: ip, 43 | RequestMethod: methods, 44 | RequestPath: path, 45 | RequestStatus: status, 46 | RequestDuration: duration.String(), 47 | } 48 | go func() { 49 | _ = repositories.LogsRepository.CreateLogRecord(¶ms) 50 | }() 51 | 52 | } 53 | -------------------------------------------------------------------------------- /server/services/service.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | authServiceModules "github.com/Xi-Yuer/cms/services/modules/auth" 5 | departmentServiceModules "github.com/Xi-Yuer/cms/services/modules/department" 6 | interfaceServiceModules "github.com/Xi-Yuer/cms/services/modules/interface" 7 | logsServiceModules "github.com/Xi-Yuer/cms/services/modules/logs" 8 | pagesServiceModules "github.com/Xi-Yuer/cms/services/modules/pages" 9 | rolesServiceModules "github.com/Xi-Yuer/cms/services/modules/roles" 10 | rolesAndInterfacesServiceModules "github.com/Xi-Yuer/cms/services/modules/rolesAndInterfaces" 11 | templateServiceModule "github.com/Xi-Yuer/cms/services/modules/template" 12 | timeTaskServiceModules "github.com/Xi-Yuer/cms/services/modules/timeTask" 13 | uploadServiceModules "github.com/Xi-Yuer/cms/services/modules/upload" 14 | userServiceModules "github.com/Xi-Yuer/cms/services/modules/users" 15 | ) 16 | 17 | var UserService = userServiceModules.UserService 18 | var AuthService = authServiceModules.AuthService 19 | var RoleService = rolesServiceModules.RolesService 20 | var PageService = pagesServiceModules.PageService 21 | var DepartmentService = departmentServiceModules.DepartmentService 22 | var InterfaceService = interfaceServiceModules.InterfaceService 23 | var LogsService = logsServiceModules.LogsService 24 | var RolesAndInterfacesService = rolesAndInterfacesServiceModules.RolesAndInterfacesService 25 | var TimeTaskService = timeTaskServiceModules.TimeTaskService 26 | var UploadService = uploadServiceModules.UploadService 27 | var TemplateService = templateServiceModule.TemplateService 28 | -------------------------------------------------------------------------------- /web/src/utils/message/index.tsx: -------------------------------------------------------------------------------- 1 | import { JointContent } from 'antd/es/message/interface'; 2 | 3 | export const MESSAGE_EVENT_NAME = 'show_message'; 4 | 5 | export enum MESSAGE_TYPES { 6 | SUCCESS = 'success', 7 | ERROR = 'error', 8 | INFO = 'info', 9 | WARNING = 'warning', 10 | LOADING = 'loading', 11 | } 12 | 13 | const dispatch = (type: MESSAGE_TYPES, content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) => { 14 | window.dispatchEvent( 15 | new CustomEvent(MESSAGE_EVENT_NAME, { 16 | detail: { 17 | params: { 18 | content, 19 | duration, 20 | onClose, 21 | }, 22 | type: type, 23 | }, 24 | }), 25 | ); 26 | }; 27 | 28 | export const message = { 29 | success(content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) { 30 | dispatch(MESSAGE_TYPES.SUCCESS, content, duration, onClose); 31 | }, 32 | error(content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) { 33 | dispatch(MESSAGE_TYPES.ERROR, content, duration, onClose); 34 | }, 35 | info(content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) { 36 | dispatch(MESSAGE_TYPES.INFO, content, duration, onClose); 37 | }, 38 | warning(content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) { 39 | dispatch(MESSAGE_TYPES.WARNING, content, duration, onClose); 40 | }, 41 | loading(content: JointContent, duration?: number | VoidFunction, onClose?: VoidFunction) { 42 | dispatch(MESSAGE_TYPES.LOADING, content, duration, onClose); 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /server/dto/modules/roles/roles.go: -------------------------------------------------------------------------------- 1 | package rolesResponsiesModules 2 | 3 | type CreateRoleParams struct { 4 | RoleName string `json:"roleName" form:"roleName" binding:"required"` 5 | Description string `json:"description" form:"description" binding:"required"` 6 | PageID []string `form:"pageID" json:"pageID"` 7 | InterfaceID []string `form:"interfaceID" json:"interfaceID"` 8 | } 9 | 10 | type UpdateRoleParams struct { 11 | RoleName string `json:"roleName" form:"roleName"` 12 | Description string `json:"description" form:"description"` 13 | PageID []string `json:"pageID" form:"pageID"` 14 | InterfaceID []string `form:"interfaceID" json:"interfaceID"` 15 | } 16 | 17 | type QueryRoleListParams struct { 18 | ID string `form:"id"` 19 | Limit int `form:"limit"` 20 | Offset int `form:"offset"` 21 | RoleName string `form:"roleName"` 22 | Description string `form:"description"` 23 | StartTime string `form:"startTime"` 24 | EndTime string `form:"endTime"` 25 | } 26 | 27 | type SingleRoleResponse struct { 28 | ID string `json:"id"` 29 | RoleName string `json:"roleName"` 30 | Description string `json:"description"` 31 | CanEdit int 32 | PageID []string `json:"pageID"` 33 | InterfaceID []string `json:"interfaceID"` 34 | CreateTime string `json:"createTime"` 35 | UpdateTime string `json:"updateTime"` 36 | } 37 | 38 | type CreateOneRecord struct { 39 | UserID string `form:"userID"` 40 | RoleID string `form:"roleID"` 41 | } 42 | 43 | type DeleteOneRecord struct { 44 | UserID string `form:"userID"` 45 | RoleID string `form:"roleID"` 46 | } 47 | -------------------------------------------------------------------------------- /micro/rpc/captcha/captchaservice/captchaservice.go: -------------------------------------------------------------------------------- 1 | // Code generated by goctl. DO NOT EDIT. 2 | // Source: captcha.proto 3 | 4 | package captchaservice 5 | 6 | import ( 7 | "context" 8 | 9 | "micro/rpc/captcha/captcha" 10 | 11 | "github.com/zeromicro/go-zero/zrpc" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | type ( 16 | GenerateCaptchaRequest = captcha.GenerateCaptchaRequest 17 | GenerateCaptchaResponse = captcha.GenerateCaptchaResponse 18 | VerifyCaptchaRequest = captcha.VerifyCaptchaRequest 19 | VerifyCaptchaResponse = captcha.VerifyCaptchaResponse 20 | 21 | CaptchaService interface { 22 | GenerateCaptcha(ctx context.Context, in *GenerateCaptchaRequest, opts ...grpc.CallOption) (*GenerateCaptchaResponse, error) 23 | VerifyCaptcha(ctx context.Context, in *VerifyCaptchaRequest, opts ...grpc.CallOption) (*VerifyCaptchaResponse, error) 24 | } 25 | 26 | defaultCaptchaService struct { 27 | cli zrpc.Client 28 | } 29 | ) 30 | 31 | func NewCaptchaService(cli zrpc.Client) CaptchaService { 32 | return &defaultCaptchaService{ 33 | cli: cli, 34 | } 35 | } 36 | 37 | func (m *defaultCaptchaService) GenerateCaptcha(ctx context.Context, in *GenerateCaptchaRequest, opts ...grpc.CallOption) (*GenerateCaptchaResponse, error) { 38 | client := captcha.NewCaptchaServiceClient(m.cli.Conn()) 39 | return client.GenerateCaptcha(ctx, in, opts...) 40 | } 41 | 42 | func (m *defaultCaptchaService) VerifyCaptcha(ctx context.Context, in *VerifyCaptchaRequest, opts ...grpc.CallOption) (*VerifyCaptchaResponse, error) { 43 | client := captcha.NewCaptchaServiceClient(m.cli.Conn()) 44 | return client.VerifyCaptcha(ctx, in, opts...) 45 | } 46 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/updateuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | 7 | "micro/rpc/user/internal/svc" 8 | "micro/rpc/user/userRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type UpdateUserLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewUpdateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserLogic { 20 | return &UpdateUserLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *UpdateUserLogic) UpdateUser(in *userRPC.UpdateUserRequest) (*userRPC.CommonResponse, error) { 28 | existLogic := NewUserIDHasBeenExistLogic(l.ctx, l.svcCtx) 29 | exist, err := existLogic.UserIDHasBeenExist(&userRPC.DeleteUserRequest{Id: in.Id}) 30 | 31 | if err != nil { 32 | return &userRPC.CommonResponse{ 33 | Ok: false, 34 | Msg: err.Error(), 35 | }, err 36 | } 37 | if !exist.Ok { 38 | return &userRPC.CommonResponse{ 39 | Ok: false, 40 | Msg: "用户不存在", 41 | }, nil 42 | } 43 | 44 | if err := l.svcCtx.GormDB.Model(&userModel.User{}).Where("id", in.Id).Updates(&userModel.User{ 45 | Password: in.Password, 46 | NickName: in.Nickname, 47 | Avatar: in.Avatar, 48 | Status: in.Status, 49 | DepartmentID: in.Department, 50 | IsAdmin: in.IsAdmin, 51 | }).Error; err != nil { 52 | return &userRPC.CommonResponse{ 53 | Ok: false, 54 | Msg: err.Error(), 55 | }, nil 56 | } 57 | return &userRPC.CommonResponse{ 58 | Ok: true, 59 | Msg: "更新成功", 60 | }, nil 61 | } 62 | -------------------------------------------------------------------------------- /micro/rpc/role/internal/logic/getrolelistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "github.com/zeromicro/go-zero/core/logx" 6 | roleModlel "micro/model/role" 7 | "micro/rpc/role/internal/svc" 8 | "micro/rpc/role/roleRPC" 9 | ) 10 | 11 | type GetRoleListLogic struct { 12 | ctx context.Context 13 | svcCtx *svc.ServiceContext 14 | logx.Logger 15 | } 16 | 17 | func NewGetRoleListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRoleListLogic { 18 | return &GetRoleListLogic{ 19 | ctx: ctx, 20 | svcCtx: svcCtx, 21 | Logger: logx.WithContext(ctx), 22 | } 23 | } 24 | 25 | func (l *GetRoleListLogic) GetRoleList(in *roleRPC.GetRoleListRequest) (*roleRPC.GetUserListResponse, error) { 26 | total := int64(0) 27 | roleList := make([]*roleModlel.Role, 0) 28 | err := l.svcCtx.GormDB.Find(&roleList).Where(roleModlel.Role{ 29 | ID: in.Id, 30 | RoleName: in.RoleName, 31 | Description: in.Description, 32 | CanEdit: in.CanEdit, 33 | }).Offset(int(in.Page)).Limit(int(in.PageSize)).Count(&total).Error 34 | 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | roleListRPC := make([]*roleRPC.GetRoleResponse, 0) 40 | 41 | for _, role := range roleList { 42 | roleListRPC = append(roleListRPC, &roleRPC.GetRoleResponse{ 43 | Id: role.ID, 44 | RoleName: role.RoleName, 45 | Description: role.Description, 46 | CanEdit: role.CanEdit, 47 | CreateTime: role.CreatedAt.String(), 48 | UpdateTime: role.UpdatedAt.String(), 49 | }) 50 | } 51 | 52 | return &roleRPC.GetUserListResponse{ 53 | Total: int32(total), 54 | RoleList: roleListRPC, 55 | }, nil 56 | } 57 | -------------------------------------------------------------------------------- /web/src/service/api/template/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | interface Field { 5 | name: string; 6 | type: string; 7 | default: string; 8 | } 9 | 10 | export interface ICreateTemplateParams { 11 | package: string; 12 | tableName: string; 13 | fields: Field[]; 14 | } 15 | 16 | export interface ICreateTemplateResponse { 17 | server: server; 18 | web: web; 19 | } 20 | 21 | export interface server { 22 | controllerFile: code; 23 | serviceFile: code; 24 | repositoryFile: code; 25 | routeFile: code; 26 | dtoFile: code; 27 | } 28 | 29 | export interface web { 30 | react: reactType; 31 | } 32 | 33 | export interface reactType { 34 | searchForm: code; 35 | table: code; 36 | tableHook: code; 37 | } 38 | 39 | export interface code { 40 | code: string; 41 | lang: string; 42 | } 43 | 44 | export interface IDownloadTemplateParams { 45 | tableName: string; 46 | controller: string | undefined; 47 | service: string | undefined; 48 | repository: string | undefined; 49 | route: string | undefined; 50 | dto: string | undefined; 51 | searchForm: string | undefined; 52 | table: string | undefined; 53 | tableHook: string | undefined; 54 | } 55 | 56 | export const createTemplateRequest = (data: ICreateTemplateParams) => { 57 | return request.post>({ 58 | url: '/template', 59 | data: data, 60 | }); 61 | }; 62 | 63 | export const downloadTemplateRequest = (data: IDownloadTemplateParams) => { 64 | return request.post({ 65 | url: '/template/download', 66 | data: data, 67 | responseType: 'blob', 68 | }); 69 | }; 70 | -------------------------------------------------------------------------------- /web/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useEffect } from 'react'; 2 | import { useAppDispatch, useAppSelector } from '@/store'; 3 | import { ConfigProvider, message } from 'antd'; 4 | import { constants } from '@/constant'; 5 | import { changeThemeMode } from '@/store/UIStore'; 6 | import { useAppRouter } from '@/hooks/useAppRoutes.tsx'; 7 | import 'dayjs/locale/zh-cn'; 8 | import dayjs from 'dayjs'; 9 | import { useTheme } from '@/theme'; 10 | import { MESSAGE_EVENT_NAME } from '@/utils'; 11 | import { MessageInstance } from 'antd/es/message/interface'; 12 | 13 | dayjs.locale('zh-cn'); 14 | 15 | const APP = () => { 16 | const [theme] = useTheme(); 17 | const [api, contextHolder] = message.useMessage(); 18 | const { langMode, themeMode } = useAppSelector((state) => state.UIStore); 19 | const dispatch = useAppDispatch(); 20 | const { element } = useAppRouter(); 21 | useEffect(() => { 22 | dispatch(changeThemeMode(themeMode)); 23 | }, [dispatch, themeMode]); 24 | 25 | useEffect(() => { 26 | const bindEvent = (e: CustomEvent | any) => { 27 | const func: keyof MessageInstance = e?.detail?.type || 'info'; 28 | const { content, duration, onClose } = e.detail?.params; 29 | api[func](content, duration, onClose); 30 | }; 31 | 32 | window.addEventListener(MESSAGE_EVENT_NAME, bindEvent); 33 | 34 | return () => { 35 | window.removeEventListener(MESSAGE_EVENT_NAME, bindEvent); 36 | }; 37 | }, [api]); 38 | 39 | return ( 40 | <> 41 | {contextHolder} 42 | 43 | {element} 44 | 45 | 46 | ); 47 | }; 48 | 49 | export default memo(APP); 50 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/createuserlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | 7 | "micro/rpc/user/internal/svc" 8 | "micro/rpc/user/userRPC" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type CreateUserLogic struct { 14 | ctx context.Context 15 | svcCtx *svc.ServiceContext 16 | logx.Logger 17 | } 18 | 19 | func NewCreateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateUserLogic { 20 | return &CreateUserLogic{ 21 | ctx: ctx, 22 | svcCtx: svcCtx, 23 | Logger: logx.WithContext(ctx), 24 | } 25 | } 26 | 27 | func (l *CreateUserLogic) CreateUser(in *userRPC.CreateUserRequest) (*userRPC.CommonResponse, error) { 28 | beenExistLogic := NewUserAccountHasBeenExistLogic(l.ctx, l.svcCtx) 29 | exist, err := beenExistLogic.UserAccountHasBeenExist(&userRPC.UserAccountHasBeenExistRequest{ 30 | Account: in.Account, 31 | }) 32 | if err != nil { 33 | return &userRPC.CommonResponse{ 34 | Ok: false, 35 | Msg: err.Error(), 36 | }, err 37 | } 38 | if exist.Ok { 39 | return &userRPC.CommonResponse{ 40 | Ok: false, 41 | Msg: "用户名已存在", 42 | }, nil 43 | } 44 | if err := l.svcCtx.GormDB.Create(&userModel.User{ 45 | ID: in.Id, 46 | Account: in.Account, 47 | Password: in.Password, 48 | Avatar: in.Avatar, 49 | NickName: in.Nickname, 50 | DepartmentID: in.Department, 51 | Status: in.Status, 52 | IsAdmin: in.IsAdmin, 53 | }).Error; err != nil { 54 | return &userRPC.CommonResponse{ 55 | Ok: false, 56 | Msg: err.Error(), 57 | }, nil 58 | } 59 | return &userRPC.CommonResponse{ 60 | Ok: true, 61 | Msg: "创建成功", 62 | }, nil 63 | } 64 | -------------------------------------------------------------------------------- /server/dto/modules/interface/interface.go: -------------------------------------------------------------------------------- 1 | package interfaceResponsiesModules 2 | 3 | type CreateInterfaceRequest struct { 4 | InterfaceName string `form:"interfaceName" binding:"required"` 5 | InterfaceMethod string `form:"interfaceMethod" binding:"required"` 6 | InterfacePageID string `form:"interfacePageID" binding:"required"` 7 | InterfacePath string `form:"interfacePath" binding:"required"` 8 | InterfaceDesc string `form:"interfaceDesc" binding:"required"` 9 | InterfaceDic string `form:"interfaceDic" binding:"required"` 10 | } 11 | 12 | type GetInterfaceResponse struct { 13 | ID string `json:"id"` 14 | InterfaceName string `json:"interfaceName"` 15 | InterfaceMethod string `json:"interfaceMethod"` 16 | InterfacePageID string `json:"interfacePageID" ` 17 | InterfacePath string `json:"interfacePath" ` 18 | InterfaceDesc string `json:"interfaceDesc"` 19 | InterfaceDic string `json:"interfaceDic"` 20 | CanEdit bool `json:"canEdit"` 21 | CreateTime string `json:"createTime"` 22 | UpdateTime string `json:"updateTime"` 23 | } 24 | 25 | type UpdateInterfaceRequest struct { 26 | InterfaceName string `form:"interfaceName" binding:"required"` 27 | InterfaceMethod string `form:"interfaceMethod" binding:"required"` 28 | InterfacePageID string `form:"interfacePageID" binding:"required"` 29 | InterfacePath string `form:"interfacePath" binding:"required"` 30 | InterfaceDesc string `form:"interfaceDesc" binding:"required"` 31 | InterfaceDic string `form:"interfaceDic" binding:"required"` 32 | } 33 | 34 | type AllInterfaceResponse struct { 35 | Key string `json:"key"` 36 | Children []*GetInterfaceResponse `json:"children"` 37 | } 38 | -------------------------------------------------------------------------------- /web/src/service/api/file/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { IHasTotalResponse, Page } from '@/service'; 3 | import { AxiosResponse } from 'axios'; 4 | 5 | export interface FileResponse { 6 | fileID: string; 7 | fileName: string; 8 | fileSize: number; 9 | uploadUser: string; 10 | uploadTime: string; 11 | } 12 | 13 | export const getFileList = (params: Page) => { 14 | return request.get>>({ 15 | url: '/upload', 16 | params, 17 | }); 18 | }; 19 | 20 | export interface IUploadFileChunk { 21 | identifier: string; 22 | file: Blob; 23 | } 24 | 25 | export const uploadFileChunk = (params: IUploadFileChunk) => { 26 | const formData = new FormData(); 27 | formData.append('file', params.file); 28 | formData.append('identifier', params.identifier); 29 | return request.post({ 30 | url: '/upload ', 31 | data: formData, 32 | }); 33 | }; 34 | 35 | export const checkFileUploadSize = (identifier: string) => { 36 | return request.post>({ 37 | url: '/upload/check', 38 | data: { 39 | identifier, 40 | }, 41 | }); 42 | }; 43 | 44 | export interface IMergeFileChunk { 45 | identifier: string; 46 | fileName: string; 47 | fileSize: number; 48 | } 49 | 50 | export const mergeFileChunk = (data: IMergeFileChunk) => { 51 | return request.post({ 52 | url: '/upload/finish', 53 | data, 54 | }); 55 | }; 56 | 57 | export const deleteFileRequest = (fileID: string) => { 58 | return request.delete({ 59 | url: `/upload/del/${fileID}`, 60 | }); 61 | }; 62 | 63 | export const getCookie = () => { 64 | return request.get({ 65 | url: '/auth/cookie', 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /server/utils/modules/exportExcel/exportExcel.go: -------------------------------------------------------------------------------- 1 | package exportExcel 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/xuri/excelize/v2" 6 | ) 7 | 8 | func ExportExcel(context *gin.Context, data [][]interface{}, fileName string) error { 9 | file := excelize.NewFile() 10 | // 创建一个工作表 11 | sheet := "sheet1" 12 | 13 | if err := file.SetSheetName(sheet, fileName); err != nil { 14 | return err 15 | } 16 | if err := file.SetColWidth(sheet, "A", "Z", 20); err != nil { 17 | return err 18 | } 19 | // 设置标题样式 20 | titleStyle, err := file.NewStyle(&excelize.Style{Font: &excelize.Font{Bold: true, VertAlign: "center"}, Fill: excelize.Fill{Color: []string{"#cccccc"}, Type: "pattern", Pattern: 1}}) 21 | 22 | // 设置数据样式 23 | dataStyle, err := file.NewStyle(&excelize.Style{Font: &excelize.Font{Size: 12}, Alignment: &excelize.Alignment{Horizontal: "left", Vertical: "center", WrapText: true}}) 24 | 25 | // 将数据写入 Excel 文件 26 | for rowIndex, row := range data { 27 | for colIndex, cell := range row { 28 | cellIndex, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1) 29 | err := file.SetCellValue(sheet, cellIndex, cell) 30 | if err != nil { 31 | return err 32 | } 33 | if rowIndex == 0 { 34 | _ = file.SetCellStyle(sheet, cellIndex, cellIndex, titleStyle) 35 | } else { 36 | _ = file.SetCellStyle(sheet, cellIndex, cellIndex, dataStyle) 37 | } 38 | } 39 | } 40 | 41 | // 将 Excel 文件内容写入 HTTP 响应 42 | context.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") 43 | context.Header("Content-Disposition", "attachment; filename="+fileName+".xlsx") 44 | context.Header("Content-Transfer-Encoding", "binary") 45 | err = file.Write(context.Writer) 46 | return err 47 | } 48 | -------------------------------------------------------------------------------- /server/template/server/service/service.tmpl: -------------------------------------------------------------------------------- 1 | package {{ .TableName }}Service 2 | 3 | import ( 4 | {{ .TableName }}Dto "{{ .Package }}/template/dto" 5 | {{ .TableName }}Repository "{{ .Package }}/template/repository" 6 | ) 7 | 8 | type ServiceInterface interface { 9 | Get{{ .TableName }}ListService(params *{{ .TableName }}Dto.{{ .TableName }}FindRequestDTO) ([]*{{ .TableName }}Dto.{{ .TableName }}FindRequestDTO, error) 10 | Create{{ .TableName }}RecordService(params *{{ .TableName }}Dto.{{ .TableName }}CreateRequestDTO) error 11 | Update{{ .TableName }}ListService(id string, params *{{ .TableName }}Dto.{{ .TableName }}UpdateRequestDTO) error 12 | Delete{{ .TableName }}RecordService(id string) error 13 | } 14 | type Service struct { 15 | repo {{ .TableName }}Repository.RepositoryInterface 16 | } 17 | 18 | func New{{ .TableName }}Service() *Service { 19 | return &Service{ 20 | repo: {{ .TableName }}Repository.New{{ .TableName }}Repository(), 21 | } 22 | } 23 | 24 | func (s Service) Get{{ .TableName }}ListService(params *{{ .TableName }}Dto.{{ .TableName }}FindRequestDTO) ([]*{{ .TableName }}Dto.{{ .TableName }}FindRequestDTO, error) { 25 | return s.repo.Get{{ .TableName }}ListRepo(params) 26 | } 27 | 28 | func (s Service) Create{{ .TableName }}RecordService(params *{{ .TableName }}Dto.{{ .TableName }}CreateRequestDTO) error { 29 | return s.repo.Create{{ .TableName }}RecordRepo(params) 30 | } 31 | 32 | func (s Service) Update{{ .TableName }}ListService(id string, params *{{ .TableName }}Dto.{{ .TableName }}UpdateRequestDTO) error { 33 | return s.repo.Update{{ .TableName }}RecordRepo(id, params) 34 | } 35 | 36 | 37 | func (s Service) Delete{{ .TableName }}RecordService(id string) error { 38 | return s.repo.Delete{{ .TableName }}RecordRepo(id) 39 | } -------------------------------------------------------------------------------- /server/dto/modules/pages/pages.go: -------------------------------------------------------------------------------- 1 | package pagesResponsiesModules 2 | 3 | type CreatePageParams struct { 4 | PageName string `form:"pageName" binding:"required"` 5 | PagePath string `form:"pagePath"` 6 | PageIcon string `form:"pageIcon" binding:"required"` 7 | PageComponent string `form:"pageComponent"` 8 | ParentPage string `form:"parentPage"` 9 | PageOrder *int `form:"pageOrder" binding:"required"` 10 | IsOutSite bool `form:"isOutSite"` 11 | OutSiteLink *string `form:"outSiteLink"` 12 | } 13 | 14 | type SinglePageResponse struct { 15 | PageID string `json:"pageID"` 16 | PageName string `json:"pageName"` 17 | PagePath string `json:"pagePath"` 18 | PageIcon string `json:"pageIcon"` 19 | PageComponent string `json:"pageComponent"` 20 | ParentPage *string `json:"parentPage"` 21 | Children []*SinglePageResponse `json:"children"` 22 | PageOrder int `json:"pageOrder"` 23 | CanEdit int `json:"canEdit"` 24 | IsOutSite bool `json:"isOutSite"` 25 | OutSiteLink *string `json:"outSiteLink"` 26 | CreatedTime string `json:"createdAt"` 27 | UpdateTime string `json:"updateTime"` 28 | } 29 | 30 | type UpdatePageRequest struct { 31 | PageName string `form:"pageName" binding:"required"` 32 | PagePath string `form:"pagePath"` 33 | PageIcon string `form:"pageIcon" binding:"required"` 34 | PageComponent string `form:"pageComponent"` 35 | PageOrder *int `form:"pageOrder" binding:"required"` 36 | IsOutSite bool `form:"isOutSite"` 37 | OutSiteLink *string `form:"outSiteLink"` 38 | } 39 | -------------------------------------------------------------------------------- /web/src/service/api/system/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/service/request'; 2 | import { AxiosResponse } from 'axios'; 3 | 4 | export interface SystemInfo { 5 | cpuUsage: number; 6 | memUsage: { 7 | total: number; // 总内存 (bytes) 8 | available: number; // 可用内存 (bytes) 9 | used: number; // 已使用内存 (bytes) 10 | usedPercent: number; // 内存使用率 (%) 11 | free: number; // 空闲内存 (bytes) 12 | active: number; // 活跃内存 (bytes) 13 | inactive: number; // 非活跃内存 (bytes) 14 | wired: number; // 已分配内存 (bytes) 15 | laundry: number; // 被回收内存 (bytes) 16 | }; 17 | } 18 | 19 | export const MenUsageMap = [ 20 | { 21 | key: 'available', 22 | label: '可用内存', 23 | }, 24 | { 25 | key: 'used', 26 | label: '已使用内存', 27 | }, 28 | { 29 | key: 'free', 30 | label: '空闲内存', 31 | }, 32 | { 33 | key: 'active', 34 | label: '活跃内存', 35 | }, 36 | { 37 | key: 'inactive', 38 | label: '非活跃内存', 39 | }, 40 | { 41 | key: 'wired', 42 | label: '已分配内存', 43 | }, 44 | { 45 | key: 'laundry', 46 | label: '被回收内存', 47 | }, 48 | ]; 49 | 50 | export interface IGitCommit { 51 | date: string; 52 | children: { 53 | author: string; 54 | commitID: string; 55 | date: string; 56 | email: string; 57 | message: string; 58 | }[]; 59 | } 60 | 61 | export const getSystemRunTimeInfoRequest = () => { 62 | return request.get>({ 63 | url: '/system', 64 | }); 65 | }; 66 | 67 | export const getGitCommitInfoRequest = () => { 68 | return request.get>({ 69 | url: '/log/commits', 70 | }); 71 | }; 72 | 73 | export const getGitCommitCountRequest = () => { 74 | return request.get>({ 75 | url: '/log/commits/count', 76 | }); 77 | }; 78 | -------------------------------------------------------------------------------- /micro/rpc/user/internal/logic/getuserlistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | userModel "micro/model/user" 6 | "micro/rpc/user/internal/svc" 7 | "micro/rpc/user/userRPC" 8 | 9 | "github.com/zeromicro/go-zero/core/logx" 10 | ) 11 | 12 | type GetUserListLogic struct { 13 | ctx context.Context 14 | svcCtx *svc.ServiceContext 15 | logx.Logger 16 | } 17 | 18 | func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserListLogic { 19 | return &GetUserListLogic{ 20 | ctx: ctx, 21 | svcCtx: svcCtx, 22 | Logger: logx.WithContext(ctx), 23 | } 24 | } 25 | 26 | func (l *GetUserListLogic) GetUserList(in *userRPC.GetUserListRequest) (*userRPC.GetUserListResponse, error) { 27 | var userList []userModel.User 28 | var total int64 29 | err := l.svcCtx.GormDB.Where(&userModel.User{ 30 | ID: in.Id, 31 | Account: in.Account, 32 | NickName: in.Nickname, 33 | Avatar: in.Avatar, 34 | Status: in.Status, 35 | DepartmentID: in.Department, 36 | IsAdmin: in.IsAdmin, 37 | }).Offset(int(in.Page)).Limit(int(in.PageSize)).Find(&userList).Count(&total).Error 38 | 39 | if err != nil { 40 | return nil, err 41 | } 42 | userListRPC := make([]*userRPC.GetUserResponse, 0) 43 | 44 | for _, v := range userList { 45 | userListRPC = append(userListRPC, &userRPC.GetUserResponse{ 46 | Id: v.ID, 47 | Account: v.Account, 48 | Nickname: v.NickName, 49 | Avatar: v.Avatar, 50 | Status: v.Status, 51 | 52 | Department: v.DepartmentID, 53 | IsAdmin: v.IsAdmin, 54 | CreateTime: v.CreatedAt.String(), 55 | UpdateTime: v.UpdatedAt.String(), 56 | }) 57 | } 58 | 59 | return &userRPC.GetUserListResponse{ 60 | UserList: userListRPC, 61 | Total: int32(total), 62 | }, nil 63 | } 64 | -------------------------------------------------------------------------------- /micro/api/user/internal/logic/getuserlistlogic.go: -------------------------------------------------------------------------------- 1 | package logic 2 | 3 | import ( 4 | "context" 5 | "micro/rpc/user/userRPC" 6 | 7 | "micro/api/user/internal/svc" 8 | "micro/api/user/internal/types" 9 | 10 | "github.com/zeromicro/go-zero/core/logx" 11 | ) 12 | 13 | type GetUserListLogic struct { 14 | logx.Logger 15 | ctx context.Context 16 | svcCtx *svc.ServiceContext 17 | } 18 | 19 | func NewGetUserListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserListLogic { 20 | return &GetUserListLogic{ 21 | Logger: logx.WithContext(ctx), 22 | ctx: ctx, 23 | svcCtx: svcCtx, 24 | } 25 | } 26 | 27 | func (l *GetUserListLogic) GetUserList(req *types.GetUserListRequest) (resp *types.GetUserListResponse, err error) { 28 | list, _ := l.svcCtx.UserService.GetUserList(l.ctx, &userRPC.GetUserListRequest{ 29 | Id: req.ID, 30 | Account: req.Account, 31 | Nickname: req.Nickname, 32 | Avatar: req.Avatar, 33 | Status: req.Status, 34 | Department: req.DepartmentID, 35 | IsAdmin: req.IsAdmin, 36 | Page: req.Offset, 37 | PageSize: req.Limit, 38 | }) 39 | userList := make([]types.UserResponse, 0) 40 | 41 | for _, user := range list.UserList { 42 | userList = append(userList, types.UserResponse{ 43 | ID: user.Id, 44 | Account: user.Account, 45 | Nickname: user.Nickname, 46 | Avatar: user.Avatar, 47 | Status: user.Status, 48 | DepartmentID: user.Department, 49 | IsAdmin: user.IsAdmin, 50 | CreateTime: user.CreateTime, 51 | UpdateTime: user.UpdateTime, 52 | }) 53 | } 54 | 55 | return &types.GetUserListResponse{ 56 | Code: 200, 57 | Msg: "成功", 58 | Data: types.UserListResponse{ 59 | Total: int(list.Total), 60 | List: userList, 61 | }, 62 | }, nil 63 | } 64 | -------------------------------------------------------------------------------- /server/dto/modules/template/template.go: -------------------------------------------------------------------------------- 1 | package templateResponsiesModules 2 | 3 | type CreateTemplateRequestParams struct { 4 | Package string `form:"package" binding:"required"` 5 | TableName string `form:"tableName" binding:"required"` 6 | Fields *[]Fields `form:"fields" binding:"required"` 7 | } 8 | 9 | type DownloadTemplateRequestParams struct { 10 | TableName string `form:"tableName" binding:"required"` 11 | Controller string `form:"controller" binding:"required"` 12 | Service string `form:"service" binding:"required"` 13 | Repository string `form:"repository" binding:"required"` 14 | Route string `form:"route" binding:"required"` 15 | DTO string `form:"dto" binding:"required"` 16 | SearchForm string `form:"searchForm" binding:"required"` 17 | Table string `form:"table" binding:"required"` 18 | TableHook string `form:"tableHook" binding:"required"` 19 | } 20 | 21 | type Fields struct { 22 | Name string `form:"name" binding:"required"` 23 | Type string `form:"type" binding:"required"` 24 | Default string `form:"default" binding:"required"` 25 | } 26 | 27 | type CreateTemplateResponse struct { 28 | Server Server `json:"server"` 29 | Web Web `json:"web"` 30 | } 31 | 32 | type Server struct { 33 | ControllerFile Code `json:"controllerFile"` 34 | ServiceFile Code `json:"serviceFile"` 35 | RepositoryFile Code `json:"repositoryFile"` 36 | RouteFile Code `json:"routeFile"` 37 | DTOFile Code `json:"dtoFile"` 38 | } 39 | type Web struct { 40 | React React `json:"react"` 41 | } 42 | 43 | type React struct { 44 | SearchForm Code `json:"searchForm"` 45 | Table Code `json:"table"` 46 | TableHook Code `json:"tableHook"` 47 | } 48 | 49 | type Code struct { 50 | Code string `json:"code"` 51 | Lang string `json:"lang"` 52 | } 53 | -------------------------------------------------------------------------------- /web/src/constant/index.ts: -------------------------------------------------------------------------------- 1 | import zhCN from 'antd/locale/zh_CN'; 2 | import enUS from 'antd/locale/en_US'; 3 | import { Locale } from 'antd/es/locale'; 4 | 5 | export const constants = { 6 | localStorage: { 7 | lang: 'lang', 8 | token: 'token', 9 | }, 10 | langMap: { 11 | zhCN: zhCN, 12 | enUS: enUS, 13 | } as Record, 14 | routePath: { 15 | login: '/login', 16 | main: '/', 17 | }, 18 | module: { 19 | ROLE: 'role', 20 | }, 21 | permissionDicMap: { 22 | EXPORT_USER: 'POST:/users/export', 23 | ADD_USER: 'POST:/users', 24 | DELETE_USER: 'DELETE:/users/:id', 25 | UPDATE_USER: 'PATCH:/users/:id', 26 | EXPORT_ROLE: 'POST:/roles/export', 27 | ADD_ROLE: 'POST:/roles', 28 | DELETE_ROLE: 'DELETE:/roles/:id', 29 | UPDATE_ROLE: 'PATCH:/roles/:id', 30 | BIND_USER: 'POST:/roles/bindUser', 31 | UNBIND_USER: 'POST:/roles/deBindUser', 32 | GET_ALL_MENU: 'GET:/pages', 33 | GET_MENU: 'GET:/pages/user', 34 | ADD_MENU: 'POST:/pages', 35 | UPDATE_MENU: 'PATCH:/pages/:id', 36 | GET_ROLE_MENU: 'GET:/pages/role/:id', 37 | ADD_DEPARTMENT: 'POST:/department', 38 | DELETE_DEPARTMENT: 'DELETE:/department/:id', 39 | UPDATE_DEPARTMENT: 'PATCH:/department/:id', 40 | GET_PAGE_INTERFACE: 'GET:/interface/page/:id', 41 | GET_ALL_INTERFACE: 'GET:/interface', 42 | GET_ALL_USER_IS_OUT_ROLE_ID: 'POST:/users/query/role/:id', 43 | ADD_PAGE_INTERFACE: 'POST:/interface', 44 | DELETE_PAGE_INTERFACE: 'DELETE:/interface/:id', 45 | UPDATE_PAGE_INTERFACE: 'PATCH:/interface/:id', 46 | CREATE_TEMPLATE: 'POST:/template', 47 | DOWNLOAD_TEMPLATE: 'POST:/template/download', 48 | UPLOAD_FILE: 'POST:/upload', 49 | DOWNLOAD_FILE: 'GET:/upload/download/aHref/:id', 50 | DELETE_FILE: 'DELETE:/upload/del/:id', 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /server/controllers/controllers.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | authControllersModules "github.com/Xi-Yuer/cms/controllers/modules/auth" 5 | commitsControllerModules "github.com/Xi-Yuer/cms/controllers/modules/commits" 6 | departmentControllerModules "github.com/Xi-Yuer/cms/controllers/modules/department" 7 | interfaceControllerModules "github.com/Xi-Yuer/cms/controllers/modules/interface" 8 | logsControllerModules "github.com/Xi-Yuer/cms/controllers/modules/logs" 9 | pagesControllerModules "github.com/Xi-Yuer/cms/controllers/modules/pages" 10 | roleControllersModules "github.com/Xi-Yuer/cms/controllers/modules/role" 11 | systemControllerModules "github.com/Xi-Yuer/cms/controllers/modules/system" 12 | templateControllerModules "github.com/Xi-Yuer/cms/controllers/modules/template" 13 | "github.com/Xi-Yuer/cms/controllers/modules/timeTask" 14 | uploadControllerModules "github.com/Xi-Yuer/cms/controllers/modules/upload" 15 | userControllersModules "github.com/Xi-Yuer/cms/controllers/modules/users" 16 | ) 17 | 18 | var UserController = userControllersModules.UserController 19 | var AuthController = authControllersModules.AuthController 20 | var RoleController = roleControllersModules.RoleController 21 | var PagesController = pagesControllerModules.PagesController 22 | var DepartmentController = departmentControllerModules.DepartmentController 23 | var InterfaceController = interfaceControllerModules.InterfaceController 24 | var LogsController = logsControllerModules.LogsController 25 | var CommitsController = commitsControllerModules.CommitsController 26 | var SystemController = systemControllerModules.SystemController 27 | var TimeTaskController = timeTaskControllerModules.TimeTaskController 28 | var UploadController = uploadControllerModules.UploadController 29 | var TemplateController = templateControllerModules.TemplateController 30 | -------------------------------------------------------------------------------- /server/repositories/modules/rolesAndInterfaces/rolesAndInterfaces.go: -------------------------------------------------------------------------------- 1 | package rolesAndInterfacesRepositoryModules 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Xi-Yuer/cms/db" 6 | "github.com/Xi-Yuer/cms/dto" 7 | ) 8 | 9 | var RolesAndInterfacesRepository = &rolesAndInterfacesRepository{} 10 | 11 | type rolesAndInterfacesRepository struct{} 12 | 13 | func (r *rolesAndInterfacesRepository) CreateRecord(params *dto.CreateRolePermissionRecordParams) error { 14 | tx, err := db.DB.Begin() 15 | if err != nil { 16 | return err 17 | } 18 | // 给角色分配接口权限,需要清空之间的数据 19 | query := `DELETE FROM roles_interfaces WHERE role_id = ?` 20 | 21 | if _, err := db.DB.Exec(query, params.RoleID); err != nil { 22 | if err := tx.Rollback(); err != nil { 23 | return err 24 | } 25 | return err 26 | } 27 | // 接口ID为空 28 | if len(params.InterfaceID) == 0 { 29 | return nil 30 | } 31 | query = `INSERT INTO roles_interfaces (role_id, interface_id) VALUES ` 32 | for _, inter := range params.InterfaceID { 33 | query += fmt.Sprintf("('%s', '%s'),", params.RoleID, inter) 34 | } 35 | query = query[:len(query)-1] // Remove the last comma and space 36 | 37 | if _, err := db.DB.Exec(query); err != nil { 38 | if err := tx.Rollback(); err != nil { 39 | return err 40 | } 41 | return err 42 | } 43 | return nil 44 | } 45 | func (r *rolesAndInterfacesRepository) GetRecordByRoleID(roleID string) ([]string, error) { 46 | query := "SELECT interface_id FROM roles_interfaces WHERE role_id = ?" 47 | rows, err := db.DB.Query(query, roleID) 48 | if err != nil { 49 | return nil, err 50 | } 51 | var interfaceIDs []string 52 | for rows.Next() { 53 | var interfaceID string 54 | if err := rows.Scan(&interfaceID); err != nil { 55 | return nil, err 56 | } 57 | interfaceIDs = append(interfaceIDs, interfaceID) 58 | 59 | } 60 | return interfaceIDs, nil 61 | } 62 | -------------------------------------------------------------------------------- /server/utils/modules/jwt/jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "errors" 5 | "github.com/Xi-Yuer/cms/config" 6 | "github.com/Xi-Yuer/cms/dto" 7 | "github.com/golang-jwt/jwt/v5" 8 | "time" 9 | ) 10 | 11 | type Jsonwebtoken struct { 12 | } 13 | 14 | func (j *Jsonwebtoken) GenerateTokenUsingHs256(jwtPayload *dto.JWTPayload) (string, error) { 15 | claim := dto.JWTPayload{ 16 | ID: jwtPayload.ID, 17 | Account: jwtPayload.Account, 18 | RoleID: jwtPayload.RoleID, 19 | IsAdmin: jwtPayload.IsAdmin, 20 | InterfaceDic: jwtPayload.InterfaceDic, 21 | DepartmentID: jwtPayload.DepartmentID, 22 | RegisteredClaims: jwt.RegisteredClaims{ 23 | Issuer: "Xi-Yuer", // 签发者 24 | Subject: jwtPayload.Account, // 签发对象 25 | ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), //过期时间 26 | NotBefore: jwt.NewNumericDate(time.Now().Add(time.Second)), //最早使用时间 27 | IssuedAt: jwt.NewNumericDate(time.Now()), //签发时间 28 | ID: jwtPayload.ID, // wt ID, 类似于盐值 29 | }, 30 | } 31 | token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim).SignedString([]byte(config.Config.APP.JWT)) 32 | return token, err 33 | } 34 | 35 | func (j *Jsonwebtoken) ParseTokenHs256(tokenStr string) (*dto.JWTPayload, error) { 36 | token, err := jwt.ParseWithClaims(tokenStr, &dto.JWTPayload{}, func(token *jwt.Token) (interface{}, error) { 37 | return []byte(config.Config.APP.JWT), nil //返回签名密钥 38 | }) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | if !token.Valid { 44 | return nil, errors.New("claim invalid") 45 | } 46 | 47 | claims, ok := token.Claims.(*dto.JWTPayload) 48 | if !ok { 49 | return nil, errors.New("invalid claim type") 50 | } 51 | 52 | return claims, nil 53 | } 54 | -------------------------------------------------------------------------------- /server/repositories/modules/repositories.go: -------------------------------------------------------------------------------- 1 | package repositories 2 | 3 | import ( 4 | commitsRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/commits" 5 | departmentRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/department" 6 | interfaceRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/interface" 7 | logsRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/logs" 8 | pagesRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/pages" 9 | rolesRepositorysModules "github.com/Xi-Yuer/cms/repositories/modules/roles" 10 | rolesAndInterfacesRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/rolesAndInterfaces" 11 | rolesAndPagesRepositoryModules "github.com/Xi-Yuer/cms/repositories/modules/rolesAndPages" 12 | uploadRepositorysModules "github.com/Xi-Yuer/cms/repositories/modules/upload" 13 | userRepositorysModules "github.com/Xi-Yuer/cms/repositories/modules/users" 14 | usersAndRolesRepositorysModules "github.com/Xi-Yuer/cms/repositories/modules/usersAndRoles" 15 | ) 16 | 17 | var UserRepositorysModules = userRepositorysModules.UserRepository 18 | var RoleRepositorysModules = rolesRepositorysModules.RolesRepository 19 | var UsersAndRolesRepositorys = usersAndRolesRepositorysModules.UsersAndRolesRepositorys 20 | var PageRepositorysModules = pagesRepositoryModules.PageRepository 21 | var RolesAndPagesRepository = rolesAndPagesRepositoryModules.RolesAndPagesRepository 22 | var DepartmentRepository = departmentRepositoryModules.DepartmentRepository 23 | var InterfaceRepository = interfaceRepositoryModules.InterfaceRepository 24 | var LogsRepository = logsRepositoryModules.LogsRepository 25 | var RolesAndInterfacesRepository = rolesAndInterfacesRepositoryModules.RolesAndInterfacesRepository 26 | var CommitsRepositoryModules = commitsRepositoryModules.CommitsRepositoryModules 27 | var UploadRepository = uploadRepositorysModules.UploadRepository 28 | -------------------------------------------------------------------------------- /server/repositories/modules/logs/logs.go: -------------------------------------------------------------------------------- 1 | package logsRepositoryModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/db" 5 | "github.com/Xi-Yuer/cms/dto" 6 | ) 7 | 8 | var LogsRepository = logsRepository{} 9 | 10 | type logsRepository struct{} 11 | 12 | func (l *logsRepository) CreateLogRecord(params *dto.CreateLogRecordRequest) error { 13 | query := "INSERT INTO logs (id, user_id, user_account, ip, method, path, status, duration) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" 14 | _, err := db.DB.Exec(query, params.ID, params.UserID, params.UserName, params.UserIP, params.RequestMethod, params.RequestPath, params.RequestStatus, params.RequestDuration) 15 | return err 16 | } 17 | 18 | func (l *logsRepository) GetLogRecords(params *dto.Page) (*dto.HasTotalResponseData, error) { 19 | count := "SELECT COUNT(*) FROM logs" 20 | var total int 21 | r, err := db.DB.Query(count) 22 | if err != nil { 23 | return nil, err 24 | } 25 | for r.Next() { 26 | err := r.Scan(&total) 27 | if err != nil { 28 | return nil, err 29 | } 30 | } 31 | query := "SELECT id, user_id, user_account, ip, method, path, status, duration, create_time FROM logs ORDER BY create_time DESC LIMIT ?, ?" 32 | var logs []*dto.GetLogRecordResponse 33 | rows, err := db.DB.Query(query, params.Offset, params.Limit) 34 | if err != nil { 35 | return nil, err 36 | } 37 | for rows.Next() { 38 | var log dto.GetLogRecordResponse 39 | err := rows.Scan(&log.ID, &log.UserID, &log.UserName, &log.UserIP, &log.RequestMethod, &log.RequestPath, &log.RequestStatus, &log.RequestDuration, &log.CreateTime) 40 | if err != nil { 41 | return nil, err 42 | } 43 | logs = append(logs, &log) 44 | } 45 | return &dto.HasTotalResponseData{ 46 | List: logs, 47 | Total: total, 48 | }, nil 49 | } 50 | 51 | func (l *logsRepository) DeleteLogRecords() { 52 | query := "DELETE FROM logs WHERE create_time < DATE_SUB(CURDATE(), INTERVAL 7 DAY)" 53 | _, _ = db.DB.Exec(query) 54 | } 55 | -------------------------------------------------------------------------------- /micro/rpc/role/desc/role.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package roleRPC; 4 | 5 | option go_package = "./roleRPC"; 6 | 7 | message CreateRoleRequest { 8 | string id = 1; 9 | string role_name = 2; 10 | string description = 3; 11 | bool can_edit = 4; 12 | } 13 | 14 | message DeleteRoleRequest { 15 | string id = 1; 16 | } 17 | message RoleIDsHasBeenExistRequest { 18 | repeated string ids = 1; 19 | } 20 | 21 | message RoleNamesHasBeenExistRequest { 22 | repeated string names = 1; 23 | } 24 | 25 | message UpdateRoleRequest { 26 | string id = 1; 27 | string role_name = 2; 28 | string description = 3; 29 | bool can_edit = 4; 30 | } 31 | 32 | message GetRoleRequest { 33 | string id = 1; 34 | } 35 | 36 | message GetRoleListRequest { 37 | string id = 1; 38 | string role_name = 2; 39 | string description = 3; 40 | bool can_edit = 4; 41 | string create_time = 9; 42 | string update_time = 10; 43 | int32 page = 11; 44 | int32 page_size = 12; 45 | } 46 | 47 | message GetRoleResponse { 48 | string id = 1; 49 | string role_name = 2; 50 | string description = 3; 51 | bool can_edit = 4; 52 | string create_time = 9; 53 | string update_time = 10; 54 | } 55 | 56 | message GetUserListResponse { 57 | int32 total = 1; 58 | repeated GetRoleResponse role_list = 2; 59 | } 60 | 61 | message CommonResponse { 62 | bool ok = 1; 63 | string msg = 2; 64 | } 65 | 66 | service RoleService { 67 | rpc CreateRole(CreateRoleRequest) returns (CommonResponse) {} 68 | rpc DeleteRole(DeleteRoleRequest) returns (CommonResponse) {} 69 | rpc UpdateRole(UpdateRoleRequest) returns (CommonResponse) {} 70 | rpc GetRole(GetRoleRequest) returns (GetRoleResponse) {} 71 | rpc GetRoleList(GetRoleListRequest) returns (GetUserListResponse) {} 72 | rpc RoleNameHasBeenExist(RoleNamesHasBeenExistRequest) returns (CommonResponse); 73 | rpc RoleIDsHasBeenExist(RoleIDsHasBeenExistRequest) returns (CommonResponse); 74 | } -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ "prod" ] 6 | 7 | jobs: 8 | 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v4 16 | with: 17 | go-version: '1.21.1' 18 | 19 | - name: Cache dependencies 20 | uses: actions/cache@v2 21 | with: 22 | path: ~/go/pkg/mod 23 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 24 | restore-keys: | 25 | ${{ runner.os }}-go- 26 | 27 | - name: Build 28 | run: go build -v -o ./build/CMS ./main.go 29 | working-directory: ./server 30 | 31 | - name: CMS Build 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: CMS 35 | path: ./server/build 36 | 37 | release: 38 | runs-on: ubuntu-latest 39 | needs: build 40 | steps: 41 | - name: Download Artifact 42 | uses: actions/download-artifact@v4 43 | with: 44 | name: CMS 45 | path: ./build 46 | 47 | - name: Create Release 48 | id: create_release 49 | uses: actions/create-release@v1 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | with: 53 | tag_name: v1.1.10 54 | release_name: Release v1.1.10 55 | body: | 56 | Release v1.1.10 57 | draft: false 58 | prerelease: false 59 | 60 | - name: Upload Release Asset 61 | id: upload-release-asset 62 | uses: actions/upload-release-asset@v1 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | with: 66 | upload_url: ${{ steps.create_release.outputs.upload_url }} 67 | asset_path: ./build/CMS 68 | asset_name: CMS 69 | asset_content_type: application/octet-stream 70 | -------------------------------------------------------------------------------- /server/utils/modules/timeTask/timeTask.go: -------------------------------------------------------------------------------- 1 | package timeTask 2 | 3 | import ( 4 | "errors" 5 | "github.com/robfig/cron/v3" 6 | ) 7 | 8 | var c *cron.Cron 9 | 10 | var TimeTask = &timeTask{ 11 | taskMap: make(map[string]struct { 12 | TaskID cron.EntryID 13 | Task *cron.Cron 14 | }), 15 | } 16 | 17 | type timeTask struct { 18 | taskMap map[string]struct { 19 | TaskID cron.EntryID 20 | Task *cron.Cron 21 | } 22 | } 23 | 24 | // AddTask 添加任务 25 | func (t *timeTask) AddTask(TimeTaskID string, corn string, task func(), status bool) error { 26 | c = cron.New(cron.WithSeconds()) 27 | id, err := c.AddFunc(corn, task) 28 | if status { 29 | go c.Start() 30 | } 31 | if err != nil { 32 | return err 33 | } 34 | t.taskMap[TimeTaskID] = struct { 35 | TaskID cron.EntryID 36 | Task *cron.Cron 37 | }{ 38 | TaskID: id, 39 | Task: c, 40 | } 41 | return nil 42 | } 43 | 44 | // StartTask 启动任务 45 | func (t *timeTask) StartTask(TimeTaskID string) error { 46 | if task, ok := t.taskMap[TimeTaskID]; ok { 47 | go task.Task.Start() 48 | return nil 49 | } else { 50 | return errors.New("任务不存在") 51 | } 52 | } 53 | 54 | // StopTask 停止任务 55 | func (t *timeTask) StopTask(TimeTaskID string) error { 56 | if task, ok := t.taskMap[TimeTaskID]; ok { 57 | task.Task.Stop() 58 | return nil 59 | } else { 60 | return errors.New("任务不存在") 61 | } 62 | } 63 | 64 | // RemoveTask 移除任务 65 | func (t *timeTask) RemoveTask(TimeTaskID string) error { 66 | if task, ok := t.taskMap[TimeTaskID]; ok { 67 | task.Task.Stop() 68 | c.Remove(task.TaskID) 69 | delete(t.taskMap, TimeTaskID) 70 | } else { 71 | return errors.New("任务不存在") 72 | } 73 | return nil 74 | } 75 | 76 | // ParseCron 校验 cron 表达式是否正确 77 | func (t *timeTask) ParseCron(s string) error { 78 | c := cron.New(cron.WithSeconds()) 79 | id, err := c.AddFunc(s, func() {}) 80 | if err != nil { 81 | return errors.New("cron 表达式错误") 82 | } else { 83 | c.Remove(id) 84 | } 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于RBAC的权限管理系统 2 | ![image](https://github.com/user-attachments/assets/01e71378-2d39-4323-81eb-1a8ef185e8fd) 3 | 4 | 5 | 简体中文 | [English](README_en.md) 6 | 7 | [![GoDoc](https://godoc.org/github.com/Xi-Yuer/GO-CMS?status.svg)](https://godoc.org/github.com/Xi-Yuer/GO-CMS) 8 | ![License](https://img.shields.io/github/license/Xi-Yuer/GO-CMS) 9 | ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/Xi-Yuer/GO-CMS) 10 | ![GitHub last commit](https://img.shields.io/github/last-commit/Xi-Yuer/GO-CMS) 11 | ![GitHub issues](https://img.shields.io/github/issues/Xi-Yuer/GO-CMS) 12 | ![GitHub forks](https://img.shields.io/github/forks/Xi-Yuer/GO-CMS) 13 | ![GitHub stars](https://img.shields.io/github/stars/Xi-Yuer/GO-CMS) 14 | 15 | 16 | ## 项目介绍 17 | 基于RBAC的权限管理系统,使用Go + Gin框架,Mysql作为数据库,前端采用 React + Antd 实现。支持国际化,主题色切换,权限控制,菜单管理,用户管理,角色管理,数据字典管理,日志管理,系统监控,代码生成器,接口文档等功能。 18 | 19 | ## 项目运行后端 20 | 21 | 1. 克隆项目到本地 22 | ```text 23 | https://github.com/Xi-Yuer/GO-CMS.git 24 | ``` 25 | 26 | 2. 安装依赖 27 | ```text 28 | cd server 29 | go mod tidy 30 | ``` 31 | 32 | 3. 修改运行项目设置环境变量 33 | server/config/config.go 34 | 35 | { 36 | NAME: "root", // 数据库用户名 37 | PASSWORD: "xxxxxx", // 密码 38 | HOST: "localhost", // 主机地址 39 | DB: "cms", // 数据库名称 40 | PORT: "3306", // 端口 41 | } 42 | 43 | 4. 创建MySQL运行环境 44 | 1. 创建一个名为 cms 的数据库, 执行SQL文件(位置:server/sql/cms_widthData.sql) 45 | 46 | 5. 运行项目 47 | ```text 48 | go run server/main.go 49 | ``` 50 | 51 | 52 | ## 项目运行前端 53 | 54 | 1. 克隆项目到本地 55 | ```text 56 | https://github.com/Xi-Yuer/GO-CMS.git 57 | ``` 58 | 59 | 2. 安装依赖 60 | ```text 61 | cd web 62 | pnpm install 63 | ``` 64 | 65 | 3. 运行项目 66 | ```text 67 | pnpm run dev 68 | ``` 69 | 70 | ## 项目部分截图 71 | ![登录](./static/login.png) 72 | 73 | ![首页](./static/dashboard.png) 74 | 75 | ![系统管理](./static/system.png) 76 | -------------------------------------------------------------------------------- /server/controllers/modules/system/system.go: -------------------------------------------------------------------------------- 1 | package systemControllerModules 2 | 3 | import ( 4 | "github.com/Xi-Yuer/cms/utils" 5 | "github.com/gin-gonic/gin" 6 | "github.com/shirou/gopsutil/cpu" 7 | "github.com/shirou/gopsutil/mem" 8 | "time" 9 | ) 10 | 11 | var SystemController = &systemController{} 12 | 13 | type systemController struct{} 14 | 15 | type MemInfo struct { 16 | Total uint64 `json:"total"` // 总内存 (bytes) 17 | Available uint64 `json:"available"` // 可用内存 (bytes) 18 | Used uint64 `json:"used"` // 已使用内存 (bytes) 19 | UsedPercent float64 `json:"usedPercent"` // 内存使用率 (%) 20 | Free uint64 `json:"free"` // 空闲内存 (bytes) 21 | Active uint64 `json:"active"` // 活跃内存 (bytes) 22 | Inactive uint64 `json:"inactive"` // 非活跃内存 (bytes) 23 | Wired uint64 `json:"wired"` // 已分配内存 (bytes) 24 | Laundry uint64 `json:"laundry"` // 被回收内存 (bytes) 25 | } 26 | 27 | var response []systemInfo 28 | 29 | func init() { 30 | initSystemInfo() 31 | } 32 | func initSystemInfo() { 33 | go func() { 34 | ticker := time.Tick(time.Second) 35 | for { 36 | select { 37 | case <-ticker: 38 | // CPU使用率 39 | CPURate, _ := cpu.Percent(time.Second, false) 40 | // 内存信息 41 | MemInfo, _ := mem.VirtualMemory() 42 | info := systemInfo{ 43 | CPUUsage: CPURate[0], 44 | MemUsage: MemInfo, 45 | } 46 | response = append(response, info) 47 | if len(response) > 20 { 48 | response = response[1:] 49 | } 50 | } 51 | } 52 | }() 53 | } 54 | 55 | type systemInfo struct { 56 | CPUUsage float64 `json:"cpuUsage"` 57 | MemUsage *mem.VirtualMemoryStat `json:"memUsage"` 58 | } 59 | 60 | // GetSystemInfo 获取系统运行信息 61 | // @Summary 获取系统运行信息 62 | // @Description 获取系统运行信息 63 | // @Tags 系统管理 64 | // @Accept json 65 | // @Produce json 66 | // @Router /system [get] 67 | func (s *systemController) GetSystemInfo(context *gin.Context) { 68 | utils.Response.Success(context, response) 69 | } 70 | -------------------------------------------------------------------------------- /server/repositories/modules/rolesAndPages/roleAndPages.go: -------------------------------------------------------------------------------- 1 | package rolesAndPagesRepositoryModules 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "github.com/Xi-Yuer/cms/db" 7 | "github.com/Xi-Yuer/cms/dto" 8 | "github.com/Xi-Yuer/cms/utils" 9 | ) 10 | 11 | var RolesAndPagesRepository = &rolesAndPagesRepository{} 12 | 13 | type rolesAndPagesRepository struct { 14 | } 15 | 16 | func (r *rolesAndPagesRepository) CreateRecord(params *dto.CreateRolePermissionRecordParams) error { 17 | // 创建角色页面权限之前需要先将之前的记录全部删除,并且要保证事务的一致性 18 | tx, err := db.DB.Begin() 19 | if err != nil { 20 | return err 21 | } 22 | _, err = db.DB.Exec("DELETE FROM roles_pages WHERE role_id = ?", params.RoleID) 23 | if err != nil { 24 | err := tx.Rollback() 25 | if err != nil { 26 | return err 27 | } 28 | return err 29 | } 30 | if len(params.PageID) == 0 { 31 | return nil 32 | } 33 | query := "INSERT INTO roles_pages (role_id, page_id) VALUES " 34 | for _, pageID := range params.PageID { 35 | query += fmt.Sprintf("('%s', '%s'),", params.RoleID, pageID) 36 | } 37 | query = query[:len(query)-1] // Remove the last comma and space 38 | 39 | _, err = db.DB.Exec(query) 40 | if err != nil { 41 | err := tx.Rollback() 42 | if err != nil { 43 | return err 44 | } 45 | return err 46 | } 47 | err = tx.Commit() 48 | if err != nil { 49 | return err 50 | } 51 | return nil 52 | } 53 | 54 | func (r *rolesAndPagesRepository) GetRecordsByRoleID(roleID string) ([]string, error) { 55 | rows, err := db.DB.Query("SELECT page_id FROM roles_pages WHERE role_id = ?", roleID) 56 | if err != nil { 57 | return nil, err 58 | } 59 | defer func(rows *sql.Rows) { 60 | err := rows.Close() 61 | if err != nil { 62 | utils.Log.Error(err) 63 | } 64 | }(rows) 65 | 66 | var pageIDs []string 67 | 68 | for rows.Next() { 69 | var pageID string 70 | err := rows.Scan(&pageID) 71 | if err != nil { 72 | return nil, err 73 | } 74 | pageIDs = append(pageIDs, pageID) 75 | } 76 | 77 | return pageIDs, nil 78 | } 79 | -------------------------------------------------------------------------------- /web/src/components/AppHeaderTab/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, memo } from 'react'; 2 | import { Popover, Tag } from 'antd'; 3 | import { Icon } from '@/components'; 4 | import '@/styles/common/index.css'; 5 | import { useAppHeaderTab } from '@/components/AppHeaderTab/hooks.tsx'; 6 | import { CSSTransition, TransitionGroup } from 'react-transition-group'; 7 | 8 | const AppHeaderTab: FC = () => { 9 | const { content, onclick, onclose, TabHeader, pathname } = useAppHeaderTab(); 10 | return ( 11 |
12 | 13 | {!!TabHeader.length && 14 | TabHeader?.map((item, index) => { 15 | return ( 16 | 26 | content(item, index)}> 27 | } 31 | key={item.pageID} 32 | closable={true} 33 | className='cursor-pointer select-none' 34 | onClose={(e) => onclose(e, item, index)} 35 | onClick={() => onclick(item)}> 36 | {item.pageName} 37 | 38 | 39 | 40 | ); 41 | })} 42 | 43 |
44 | ); 45 | }; 46 | 47 | export default memo(AppHeaderTab); 48 | -------------------------------------------------------------------------------- /server/utils/modules/captcha/captcha.go: -------------------------------------------------------------------------------- 1 | package captcha 2 | 3 | import ( 4 | "bytes" 5 | "github.com/dchest/captcha" 6 | "github.com/gin-contrib/sessions" 7 | "github.com/gin-gonic/gin" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | type Captcha struct{} 13 | 14 | func (c *Captcha) GenerateCaptcha(context *gin.Context, length ...int) { 15 | l := captcha.DefaultLen 16 | w, h := 107, 36 17 | if len(length) == 1 { 18 | l = length[0] 19 | } 20 | if len(length) == 2 { 21 | w = length[1] 22 | } 23 | if len(length) == 3 { 24 | h = length[2] 25 | } 26 | captchaId := captcha.NewLen(l) 27 | session := sessions.Default(context) 28 | session.Set("captcha", captchaId) 29 | _ = session.Save() 30 | _ = serve(context.Writer, context.Request, captchaId, ".png", "zh", false, w, h) 31 | } 32 | func (c *Captcha) VerifyCaptcha(context *gin.Context, code string) bool { 33 | session := sessions.Default(context) 34 | if captchaId := session.Get("captcha"); captchaId != nil { 35 | session.Delete("captcha") 36 | _ = session.Save() 37 | if captcha.VerifyString(captchaId.(string), code) { 38 | return true 39 | } else { 40 | return false 41 | } 42 | } else { 43 | return false 44 | } 45 | } 46 | 47 | func serve(w http.ResponseWriter, r *http.Request, id, ext, lang string, download bool, width, height int) error { 48 | w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") 49 | w.Header().Set("Pragma", "no-cache") 50 | w.Header().Set("Expires", "0") 51 | 52 | var content bytes.Buffer 53 | switch ext { 54 | case ".png": 55 | w.Header().Set("Content-Type", "image/png") 56 | _ = captcha.WriteImage(&content, id, width, height) 57 | case ".wav": 58 | w.Header().Set("Content-Type", "audio/x-wav") 59 | _ = captcha.WriteAudio(&content, id, lang) 60 | default: 61 | return captcha.ErrNotFound 62 | } 63 | 64 | if download { 65 | w.Header().Set("Content-Type", "application/octet-stream") 66 | } 67 | http.ServeContent(w, r, id+ext, time.Time{}, bytes.NewReader(content.Bytes())) 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /micro/shared/gorm/gorm.go: -------------------------------------------------------------------------------- 1 | package gormDB 2 | 3 | import ( 4 | "gorm.io/driver/mysql" 5 | "gorm.io/gorm" 6 | "gorm.io/gorm/logger" 7 | "log" 8 | roleModlel "micro/model/role" 9 | userModel "micro/model/user" 10 | "os" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | var ( 16 | DB *gorm.DB 17 | once sync.Once 18 | ) 19 | 20 | func NewGorm() *gorm.DB { 21 | once.Do(func() { 22 | newLogger := logger.New( 23 | log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer 24 | logger.Config{ 25 | SlowThreshold: time.Second, // Slow SQL threshold 26 | LogLevel: logger.Silent, // Log level 27 | IgnoreRecordNotFoundError: false, // Ignore ErrRecordNotFound error for logger 28 | ParameterizedQueries: false, // Don't include params in the SQL log 29 | Colorful: false, // Disable color 30 | }, 31 | ) 32 | db, err := gorm.Open(mysql.New(mysql.Config{ 33 | DSN: "root:2214380963Wx!!@tcp(mysql:3306)/micro_cms?charset=utf8&parseTime=True&loc=Local", // 数据源名称 34 | DefaultStringSize: 256, // string 类型字段的默认长度 35 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持 36 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引 37 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列 38 | SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置 39 | }), &gorm.Config{ 40 | Logger: newLogger, 41 | }) 42 | 43 | if err := db.AutoMigrate(&userModel.User{}, &roleModlel.Role{}); err != nil { 44 | log.Fatalf("数据库迁移失败: %v", err) 45 | } 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | DB = db 51 | }) 52 | 53 | return DB 54 | } 55 | --------------------------------------------------------------------------------