From 632d32c0c6bd0767fa0e78e38e19893399bfc5b4 Mon Sep 17 00:00:00 2001
From: haotian <2421912570@qq.com>
Date: Fri, 25 Jul 2025 10:03:06 +0800
Subject: [PATCH] original_project
---
README.md | 146 ++++
admin-api/.gitignore | 8 +
admin-api/Dockerfile | 27 +
admin-api/application/__init__.py | 2 +
admin-api/application/asgi.py | 16 +
admin-api/application/settings.py | 110 +++
admin-api/application/urls.py | 21 +
admin-api/application/wsgi.py | 15 +
admin-api/build.sh | 3 +
admin-api/config.py | 68 ++
admin-api/createDatabase.py | 32 +
admin-api/crontab/myScheduler.py | 20 +
admin-api/database/core.py | 25 +
admin-api/docker-compose.yaml | 21 +
admin-api/gunicorn.conf.py | 42 +
admin-api/initDatabase.py | 386 +++++++++
admin-api/manage.py | 20 +
admin-api/middleware/myAuthorization.py | 50 ++
admin-api/middleware/myMiddleware.py | 80 ++
admin-api/requirements.txt | Bin 0 -> 994 bytes
admin-api/utils/myDataUtils.py | 291 +++++++
admin-api/utils/myEncrypt.py | 59 ++
admin-api/utils/myEnum.py | 54 ++
admin-api/utils/myResFormat.py | 48 ++
admin-api/utils/mySnowflake.py | 122 +++
admin-api/utils/myTimeFormat.py | 41 +
admin-api/web/__init__.py | 0
admin-api/web/admin.py | 3 +
admin-api/web/apps.py | 6 +
admin-api/web/captchaImage.py | 109 +++
admin-api/web/migrations/0001_initial.py | 37 +
.../web/migrations/0002_auto_20240531_1456.py | 174 ++++
.../web/migrations/0003_auto_20240531_2104.py | 21 +
.../web/migrations/0004_auto_20240614_1628.py | 58 ++
admin-api/web/migrations/__init__.py | 0
admin-api/web/models.py | 108 +++
admin-api/web/paginator.py | 42 +
admin-api/web/serializer.py | 55 ++
admin-api/web/static/fontBold.ttf | Bin 0 -> 705684 bytes
admin-api/web/system/urls.py | 48 ++
admin-api/web/system/views.py | 776 ++++++++++++++++++
admin-api/web/tests.py | 3 +
admin-api/web/urls.py | 10 +
admin-api/web/views.py | 199 +++++
admin-ui/.env.development | 9 +
admin-ui/.env.production | 11 +
admin-ui/.env.staging | 11 +
admin-ui/.gitignore | 23 +
admin-ui/Dockerfile | 4 +
admin-ui/LICENSE | 0
admin-ui/build.sh | 4 +
admin-ui/docker-compose.yaml | 18 +
admin-ui/html/ie.html | 46 ++
admin-ui/index.html | 216 +++++
admin-ui/package.json | 45 +
admin-ui/public/favicon.ico | Bin 0 -> 5663 bytes
admin-ui/src/App.vue | 15 +
admin-ui/src/api/login.js | 51 ++
admin-ui/src/api/menu.js | 9 +
admin-ui/src/api/monitor/cache.js | 57 ++
admin-ui/src/api/monitor/job.js | 71 ++
admin-ui/src/api/monitor/jobLog.js | 26 +
admin-ui/src/api/monitor/logininfor.js | 34 +
admin-ui/src/api/monitor/online.js | 18 +
admin-ui/src/api/monitor/operlog.js | 26 +
admin-ui/src/api/monitor/server.js | 9 +
admin-ui/src/api/system/config.js | 60 ++
admin-ui/src/api/system/dept.js | 52 ++
admin-ui/src/api/system/dict/data.js | 52 ++
admin-ui/src/api/system/dict/type.js | 60 ++
admin-ui/src/api/system/menu.js | 60 ++
admin-ui/src/api/system/notice.js | 44 +
admin-ui/src/api/system/post.js | 44 +
admin-ui/src/api/system/role.js | 119 +++
admin-ui/src/api/system/user.js | 135 +++
admin-ui/src/api/tool/gen.js | 76 ++
admin-ui/src/assets/icons/svg/404.svg | 1 +
admin-ui/src/assets/icons/svg/bug.svg | 1 +
admin-ui/src/assets/icons/svg/build.svg | 1 +
admin-ui/src/assets/icons/svg/button.svg | 1 +
admin-ui/src/assets/icons/svg/cascader.svg | 1 +
admin-ui/src/assets/icons/svg/chart.svg | 1 +
admin-ui/src/assets/icons/svg/checkbox.svg | 1 +
admin-ui/src/assets/icons/svg/clipboard.svg | 1 +
admin-ui/src/assets/icons/svg/code.svg | 1 +
admin-ui/src/assets/icons/svg/color.svg | 1 +
admin-ui/src/assets/icons/svg/component.svg | 1 +
admin-ui/src/assets/icons/svg/dashboard.svg | 1 +
admin-ui/src/assets/icons/svg/date-range.svg | 1 +
admin-ui/src/assets/icons/svg/date.svg | 1 +
admin-ui/src/assets/icons/svg/dict.svg | 1 +
.../src/assets/icons/svg/documentation.svg | 1 +
admin-ui/src/assets/icons/svg/download.svg | 1 +
admin-ui/src/assets/icons/svg/drag.svg | 1 +
admin-ui/src/assets/icons/svg/druid.svg | 1 +
admin-ui/src/assets/icons/svg/edit.svg | 1 +
admin-ui/src/assets/icons/svg/education.svg | 1 +
admin-ui/src/assets/icons/svg/email.svg | 1 +
admin-ui/src/assets/icons/svg/example.svg | 1 +
admin-ui/src/assets/icons/svg/excel.svg | 1 +
.../src/assets/icons/svg/exit-fullscreen.svg | 1 +
admin-ui/src/assets/icons/svg/eye-open.svg | 1 +
admin-ui/src/assets/icons/svg/eye.svg | 1 +
admin-ui/src/assets/icons/svg/form.svg | 1 +
admin-ui/src/assets/icons/svg/fullscreen.svg | 1 +
admin-ui/src/assets/icons/svg/github.svg | 1 +
admin-ui/src/assets/icons/svg/guide.svg | 1 +
admin-ui/src/assets/icons/svg/icon.svg | 1 +
admin-ui/src/assets/icons/svg/input.svg | 1 +
.../src/assets/icons/svg/international.svg | 1 +
admin-ui/src/assets/icons/svg/job.svg | 1 +
admin-ui/src/assets/icons/svg/language.svg | 1 +
admin-ui/src/assets/icons/svg/link.svg | 1 +
admin-ui/src/assets/icons/svg/list.svg | 1 +
admin-ui/src/assets/icons/svg/lock.svg | 1 +
admin-ui/src/assets/icons/svg/log.svg | 1 +
admin-ui/src/assets/icons/svg/logininfor.svg | 1 +
admin-ui/src/assets/icons/svg/message.svg | 1 +
admin-ui/src/assets/icons/svg/money.svg | 1 +
admin-ui/src/assets/icons/svg/monitor.svg | 2 +
admin-ui/src/assets/icons/svg/nested.svg | 1 +
admin-ui/src/assets/icons/svg/number.svg | 1 +
admin-ui/src/assets/icons/svg/online.svg | 1 +
admin-ui/src/assets/icons/svg/password.svg | 1 +
admin-ui/src/assets/icons/svg/pdf.svg | 1 +
admin-ui/src/assets/icons/svg/people.svg | 1 +
admin-ui/src/assets/icons/svg/peoples.svg | 1 +
admin-ui/src/assets/icons/svg/phone.svg | 1 +
admin-ui/src/assets/icons/svg/post.svg | 1 +
admin-ui/src/assets/icons/svg/qq.svg | 1 +
admin-ui/src/assets/icons/svg/question.svg | 1 +
admin-ui/src/assets/icons/svg/radio.svg | 1 +
admin-ui/src/assets/icons/svg/rate.svg | 1 +
admin-ui/src/assets/icons/svg/redis-list.svg | 2 +
admin-ui/src/assets/icons/svg/redis.svg | 1 +
admin-ui/src/assets/icons/svg/row.svg | 1 +
admin-ui/src/assets/icons/svg/search.svg | 1 +
admin-ui/src/assets/icons/svg/select.svg | 1 +
admin-ui/src/assets/icons/svg/server.svg | 1 +
admin-ui/src/assets/icons/svg/shopping.svg | 1 +
admin-ui/src/assets/icons/svg/size.svg | 1 +
admin-ui/src/assets/icons/svg/skill.svg | 1 +
admin-ui/src/assets/icons/svg/slider.svg | 1 +
admin-ui/src/assets/icons/svg/star.svg | 1 +
admin-ui/src/assets/icons/svg/swagger.svg | 1 +
admin-ui/src/assets/icons/svg/switch.svg | 1 +
admin-ui/src/assets/icons/svg/system.svg | 2 +
admin-ui/src/assets/icons/svg/tab.svg | 1 +
admin-ui/src/assets/icons/svg/table.svg | 1 +
admin-ui/src/assets/icons/svg/textarea.svg | 1 +
admin-ui/src/assets/icons/svg/theme.svg | 1 +
admin-ui/src/assets/icons/svg/time-range.svg | 1 +
admin-ui/src/assets/icons/svg/time.svg | 1 +
admin-ui/src/assets/icons/svg/tool.svg | 1 +
admin-ui/src/assets/icons/svg/tree-table.svg | 1 +
admin-ui/src/assets/icons/svg/tree.svg | 1 +
admin-ui/src/assets/icons/svg/upload.svg | 1 +
admin-ui/src/assets/icons/svg/user.svg | 1 +
admin-ui/src/assets/icons/svg/validCode.svg | 1 +
admin-ui/src/assets/icons/svg/wechat.svg | 1 +
admin-ui/src/assets/icons/svg/zip.svg | 1 +
admin-ui/src/assets/images/dark.svg | 39 +
admin-ui/src/assets/images/light.svg | 39 +
admin-ui/src/assets/styles/btn.scss | 99 +++
admin-ui/src/assets/styles/element-ui.scss | 96 +++
admin-ui/src/assets/styles/index.scss | 184 +++++
admin-ui/src/assets/styles/mixin.scss | 66 ++
admin-ui/src/assets/styles/ruoyi.scss | 281 +++++++
admin-ui/src/assets/styles/sidebar.scss | 238 ++++++
admin-ui/src/assets/styles/transition.scss | 49 ++
.../src/assets/styles/variables.module.scss | 65 ++
admin-ui/src/components/Breadcrumb/index.vue | 66 ++
admin-ui/src/components/Crontab/day.vue | 174 ++++
admin-ui/src/components/Crontab/hour.vue | 127 +++
admin-ui/src/components/Crontab/index.vue | 310 +++++++
admin-ui/src/components/Crontab/min.vue | 126 +++
admin-ui/src/components/Crontab/month.vue | 141 ++++
admin-ui/src/components/Crontab/result.vue | 540 ++++++++++++
admin-ui/src/components/Crontab/second.vue | 128 +++
admin-ui/src/components/Crontab/week.vue | 197 +++++
admin-ui/src/components/Crontab/year.vue | 149 ++++
admin-ui/src/components/DictTag/index.vue | 82 ++
admin-ui/src/components/Editor/index.vue | 251 ++++++
admin-ui/src/components/FileUpload/index.vue | 207 +++++
admin-ui/src/components/Hamburger/index.vue | 41 +
.../src/components/HeaderSearch/index.vue | 187 +++++
admin-ui/src/components/IconSelect/index.vue | 111 +++
.../src/components/IconSelect/requireIcons.js | 8 +
.../src/components/ImagePreview/index.vue | 92 +++
admin-ui/src/components/ImageUpload/index.vue | 213 +++++
admin-ui/src/components/Pagination/index.vue | 105 +++
admin-ui/src/components/ParentView/index.vue | 3 +
.../src/components/RightToolbar/index.vue | 134 +++
admin-ui/src/components/SvgIcon/index.vue | 53 ++
admin-ui/src/components/SvgIcon/svgicon.js | 10 +
admin-ui/src/components/TopNav/index.vue | 214 +++++
admin-ui/src/components/TreeSelect/index.vue | 156 ++++
admin-ui/src/components/iFrame/index.vue | 31 +
admin-ui/src/directive/common/copyText.js | 66 ++
admin-ui/src/directive/index.js | 9 +
admin-ui/src/directive/permission/hasPermi.js | 28 +
admin-ui/src/directive/permission/hasRole.js | 28 +
admin-ui/src/layout/components/AppMain.vue | 68 ++
.../layout/components/IframeToggle/index.vue | 25 +
.../src/layout/components/InnerLink/index.vue | 24 +
admin-ui/src/layout/components/Navbar.vue | 174 ++++
.../src/layout/components/Settings/index.vue | 205 +++++
.../src/layout/components/Sidebar/Link.vue | 40 +
.../src/layout/components/Sidebar/Logo.vue | 81 ++
.../layout/components/Sidebar/SidebarItem.vue | 102 +++
.../src/layout/components/Sidebar/index.vue | 54 ++
.../layout/components/TagsView/ScrollPane.vue | 105 +++
.../src/layout/components/TagsView/index.vue | 338 ++++++++
admin-ui/src/layout/components/index.js | 4 +
admin-ui/src/layout/index.vue | 111 +++
admin-ui/src/main.js | 84 ++
admin-ui/src/permission.js | 79 ++
admin-ui/src/plugins/auth.js | 60 ++
admin-ui/src/plugins/cache.js | 77 ++
admin-ui/src/plugins/download.js | 79 ++
admin-ui/src/plugins/index.js | 18 +
admin-ui/src/plugins/modal.js | 82 ++
admin-ui/src/plugins/tab.js | 69 ++
admin-ui/src/router/index.js | 134 +++
admin-ui/src/settings.js | 47 ++
admin-ui/src/store/index.js | 3 +
admin-ui/src/store/modules/app.js | 46 ++
admin-ui/src/store/modules/dict.js | 57 ++
admin-ui/src/store/modules/permission.js | 142 ++++
admin-ui/src/store/modules/settings.js | 38 +
admin-ui/src/store/modules/tagsView.js | 182 ++++
admin-ui/src/store/modules/user.js | 72 ++
admin-ui/src/utils/auth.js | 15 +
admin-ui/src/utils/dict.js | 24 +
admin-ui/src/utils/dynamicTitle.js | 15 +
admin-ui/src/utils/errorCode.js | 6 +
admin-ui/src/utils/index.js | 390 +++++++++
admin-ui/src/utils/jsencrypt.js | 30 +
admin-ui/src/utils/permission.js | 51 ++
admin-ui/src/utils/request.js | 152 ++++
admin-ui/src/utils/ruoyi.js | 246 ++++++
admin-ui/src/utils/scroll-to.js | 58 ++
admin-ui/src/utils/theme.js | 49 ++
admin-ui/src/utils/validate.js | 93 +++
admin-ui/src/views/error/401.vue | 82 ++
admin-ui/src/views/error/404.vue | 227 +++++
admin-ui/src/views/index.vue | 23 +
admin-ui/src/views/login.vue | 228 +++++
admin-ui/src/views/redirect/index.vue | 14 +
admin-ui/src/views/register.vue | 216 +++++
admin-ui/src/views/system/dept/index.vue | 381 +++++++++
admin-ui/src/views/system/menu/index.vue | 462 +++++++++++
admin-ui/src/views/system/role/authUser.vue | 171 ++++
admin-ui/src/views/system/role/index.vue | 630 ++++++++++++++
admin-ui/src/views/system/role/selectUser.vue | 138 ++++
admin-ui/src/views/system/user/authRole.vue | 112 +++
admin-ui/src/views/system/user/index.vue | 482 +++++++++++
.../src/views/system/user/profile/index.vue | 80 ++
.../views/system/user/profile/resetPwd.vue | 57 ++
.../views/system/user/profile/userAvatar.vue | 171 ++++
.../views/system/user/profile/userInfo.vue | 62 ++
admin-ui/vite.config.js | 64 ++
admin-ui/vite/plugins/auto-import.js | 12 +
admin-ui/vite/plugins/compression.js | 28 +
admin-ui/vite/plugins/index.js | 15 +
admin-ui/vite/plugins/setup-extend.js | 5 +
admin-ui/vite/plugins/svg-icon.js | 10 +
267 files changed, 17391 insertions(+)
create mode 100644 README.md
create mode 100644 admin-api/.gitignore
create mode 100644 admin-api/Dockerfile
create mode 100644 admin-api/application/__init__.py
create mode 100644 admin-api/application/asgi.py
create mode 100644 admin-api/application/settings.py
create mode 100644 admin-api/application/urls.py
create mode 100644 admin-api/application/wsgi.py
create mode 100644 admin-api/build.sh
create mode 100644 admin-api/config.py
create mode 100644 admin-api/createDatabase.py
create mode 100644 admin-api/crontab/myScheduler.py
create mode 100644 admin-api/database/core.py
create mode 100644 admin-api/docker-compose.yaml
create mode 100644 admin-api/gunicorn.conf.py
create mode 100644 admin-api/initDatabase.py
create mode 100644 admin-api/manage.py
create mode 100644 admin-api/middleware/myAuthorization.py
create mode 100644 admin-api/middleware/myMiddleware.py
create mode 100644 admin-api/requirements.txt
create mode 100644 admin-api/utils/myDataUtils.py
create mode 100644 admin-api/utils/myEncrypt.py
create mode 100644 admin-api/utils/myEnum.py
create mode 100644 admin-api/utils/myResFormat.py
create mode 100644 admin-api/utils/mySnowflake.py
create mode 100644 admin-api/utils/myTimeFormat.py
create mode 100644 admin-api/web/__init__.py
create mode 100644 admin-api/web/admin.py
create mode 100644 admin-api/web/apps.py
create mode 100644 admin-api/web/captchaImage.py
create mode 100644 admin-api/web/migrations/0001_initial.py
create mode 100644 admin-api/web/migrations/0002_auto_20240531_1456.py
create mode 100644 admin-api/web/migrations/0003_auto_20240531_2104.py
create mode 100644 admin-api/web/migrations/0004_auto_20240614_1628.py
create mode 100644 admin-api/web/migrations/__init__.py
create mode 100644 admin-api/web/models.py
create mode 100644 admin-api/web/paginator.py
create mode 100644 admin-api/web/serializer.py
create mode 100644 admin-api/web/static/fontBold.ttf
create mode 100644 admin-api/web/system/urls.py
create mode 100644 admin-api/web/system/views.py
create mode 100644 admin-api/web/tests.py
create mode 100644 admin-api/web/urls.py
create mode 100644 admin-api/web/views.py
create mode 100644 admin-ui/.env.development
create mode 100644 admin-ui/.env.production
create mode 100644 admin-ui/.env.staging
create mode 100644 admin-ui/.gitignore
create mode 100644 admin-ui/Dockerfile
create mode 100644 admin-ui/LICENSE
create mode 100644 admin-ui/build.sh
create mode 100644 admin-ui/docker-compose.yaml
create mode 100644 admin-ui/html/ie.html
create mode 100644 admin-ui/index.html
create mode 100644 admin-ui/package.json
create mode 100644 admin-ui/public/favicon.ico
create mode 100644 admin-ui/src/App.vue
create mode 100644 admin-ui/src/api/login.js
create mode 100644 admin-ui/src/api/menu.js
create mode 100644 admin-ui/src/api/monitor/cache.js
create mode 100644 admin-ui/src/api/monitor/job.js
create mode 100644 admin-ui/src/api/monitor/jobLog.js
create mode 100644 admin-ui/src/api/monitor/logininfor.js
create mode 100644 admin-ui/src/api/monitor/online.js
create mode 100644 admin-ui/src/api/monitor/operlog.js
create mode 100644 admin-ui/src/api/monitor/server.js
create mode 100644 admin-ui/src/api/system/config.js
create mode 100644 admin-ui/src/api/system/dept.js
create mode 100644 admin-ui/src/api/system/dict/data.js
create mode 100644 admin-ui/src/api/system/dict/type.js
create mode 100644 admin-ui/src/api/system/menu.js
create mode 100644 admin-ui/src/api/system/notice.js
create mode 100644 admin-ui/src/api/system/post.js
create mode 100644 admin-ui/src/api/system/role.js
create mode 100644 admin-ui/src/api/system/user.js
create mode 100644 admin-ui/src/api/tool/gen.js
create mode 100644 admin-ui/src/assets/icons/svg/404.svg
create mode 100644 admin-ui/src/assets/icons/svg/bug.svg
create mode 100644 admin-ui/src/assets/icons/svg/build.svg
create mode 100644 admin-ui/src/assets/icons/svg/button.svg
create mode 100644 admin-ui/src/assets/icons/svg/cascader.svg
create mode 100644 admin-ui/src/assets/icons/svg/chart.svg
create mode 100644 admin-ui/src/assets/icons/svg/checkbox.svg
create mode 100644 admin-ui/src/assets/icons/svg/clipboard.svg
create mode 100644 admin-ui/src/assets/icons/svg/code.svg
create mode 100644 admin-ui/src/assets/icons/svg/color.svg
create mode 100644 admin-ui/src/assets/icons/svg/component.svg
create mode 100644 admin-ui/src/assets/icons/svg/dashboard.svg
create mode 100644 admin-ui/src/assets/icons/svg/date-range.svg
create mode 100644 admin-ui/src/assets/icons/svg/date.svg
create mode 100644 admin-ui/src/assets/icons/svg/dict.svg
create mode 100644 admin-ui/src/assets/icons/svg/documentation.svg
create mode 100644 admin-ui/src/assets/icons/svg/download.svg
create mode 100644 admin-ui/src/assets/icons/svg/drag.svg
create mode 100644 admin-ui/src/assets/icons/svg/druid.svg
create mode 100644 admin-ui/src/assets/icons/svg/edit.svg
create mode 100644 admin-ui/src/assets/icons/svg/education.svg
create mode 100644 admin-ui/src/assets/icons/svg/email.svg
create mode 100644 admin-ui/src/assets/icons/svg/example.svg
create mode 100644 admin-ui/src/assets/icons/svg/excel.svg
create mode 100644 admin-ui/src/assets/icons/svg/exit-fullscreen.svg
create mode 100644 admin-ui/src/assets/icons/svg/eye-open.svg
create mode 100644 admin-ui/src/assets/icons/svg/eye.svg
create mode 100644 admin-ui/src/assets/icons/svg/form.svg
create mode 100644 admin-ui/src/assets/icons/svg/fullscreen.svg
create mode 100644 admin-ui/src/assets/icons/svg/github.svg
create mode 100644 admin-ui/src/assets/icons/svg/guide.svg
create mode 100644 admin-ui/src/assets/icons/svg/icon.svg
create mode 100644 admin-ui/src/assets/icons/svg/input.svg
create mode 100644 admin-ui/src/assets/icons/svg/international.svg
create mode 100644 admin-ui/src/assets/icons/svg/job.svg
create mode 100644 admin-ui/src/assets/icons/svg/language.svg
create mode 100644 admin-ui/src/assets/icons/svg/link.svg
create mode 100644 admin-ui/src/assets/icons/svg/list.svg
create mode 100644 admin-ui/src/assets/icons/svg/lock.svg
create mode 100644 admin-ui/src/assets/icons/svg/log.svg
create mode 100644 admin-ui/src/assets/icons/svg/logininfor.svg
create mode 100644 admin-ui/src/assets/icons/svg/message.svg
create mode 100644 admin-ui/src/assets/icons/svg/money.svg
create mode 100644 admin-ui/src/assets/icons/svg/monitor.svg
create mode 100644 admin-ui/src/assets/icons/svg/nested.svg
create mode 100644 admin-ui/src/assets/icons/svg/number.svg
create mode 100644 admin-ui/src/assets/icons/svg/online.svg
create mode 100644 admin-ui/src/assets/icons/svg/password.svg
create mode 100644 admin-ui/src/assets/icons/svg/pdf.svg
create mode 100644 admin-ui/src/assets/icons/svg/people.svg
create mode 100644 admin-ui/src/assets/icons/svg/peoples.svg
create mode 100644 admin-ui/src/assets/icons/svg/phone.svg
create mode 100644 admin-ui/src/assets/icons/svg/post.svg
create mode 100644 admin-ui/src/assets/icons/svg/qq.svg
create mode 100644 admin-ui/src/assets/icons/svg/question.svg
create mode 100644 admin-ui/src/assets/icons/svg/radio.svg
create mode 100644 admin-ui/src/assets/icons/svg/rate.svg
create mode 100644 admin-ui/src/assets/icons/svg/redis-list.svg
create mode 100644 admin-ui/src/assets/icons/svg/redis.svg
create mode 100644 admin-ui/src/assets/icons/svg/row.svg
create mode 100644 admin-ui/src/assets/icons/svg/search.svg
create mode 100644 admin-ui/src/assets/icons/svg/select.svg
create mode 100644 admin-ui/src/assets/icons/svg/server.svg
create mode 100644 admin-ui/src/assets/icons/svg/shopping.svg
create mode 100644 admin-ui/src/assets/icons/svg/size.svg
create mode 100644 admin-ui/src/assets/icons/svg/skill.svg
create mode 100644 admin-ui/src/assets/icons/svg/slider.svg
create mode 100644 admin-ui/src/assets/icons/svg/star.svg
create mode 100644 admin-ui/src/assets/icons/svg/swagger.svg
create mode 100644 admin-ui/src/assets/icons/svg/switch.svg
create mode 100644 admin-ui/src/assets/icons/svg/system.svg
create mode 100644 admin-ui/src/assets/icons/svg/tab.svg
create mode 100644 admin-ui/src/assets/icons/svg/table.svg
create mode 100644 admin-ui/src/assets/icons/svg/textarea.svg
create mode 100644 admin-ui/src/assets/icons/svg/theme.svg
create mode 100644 admin-ui/src/assets/icons/svg/time-range.svg
create mode 100644 admin-ui/src/assets/icons/svg/time.svg
create mode 100644 admin-ui/src/assets/icons/svg/tool.svg
create mode 100644 admin-ui/src/assets/icons/svg/tree-table.svg
create mode 100644 admin-ui/src/assets/icons/svg/tree.svg
create mode 100644 admin-ui/src/assets/icons/svg/upload.svg
create mode 100644 admin-ui/src/assets/icons/svg/user.svg
create mode 100644 admin-ui/src/assets/icons/svg/validCode.svg
create mode 100644 admin-ui/src/assets/icons/svg/wechat.svg
create mode 100644 admin-ui/src/assets/icons/svg/zip.svg
create mode 100644 admin-ui/src/assets/images/dark.svg
create mode 100644 admin-ui/src/assets/images/light.svg
create mode 100644 admin-ui/src/assets/styles/btn.scss
create mode 100644 admin-ui/src/assets/styles/element-ui.scss
create mode 100644 admin-ui/src/assets/styles/index.scss
create mode 100644 admin-ui/src/assets/styles/mixin.scss
create mode 100644 admin-ui/src/assets/styles/ruoyi.scss
create mode 100644 admin-ui/src/assets/styles/sidebar.scss
create mode 100644 admin-ui/src/assets/styles/transition.scss
create mode 100644 admin-ui/src/assets/styles/variables.module.scss
create mode 100644 admin-ui/src/components/Breadcrumb/index.vue
create mode 100644 admin-ui/src/components/Crontab/day.vue
create mode 100644 admin-ui/src/components/Crontab/hour.vue
create mode 100644 admin-ui/src/components/Crontab/index.vue
create mode 100644 admin-ui/src/components/Crontab/min.vue
create mode 100644 admin-ui/src/components/Crontab/month.vue
create mode 100644 admin-ui/src/components/Crontab/result.vue
create mode 100644 admin-ui/src/components/Crontab/second.vue
create mode 100644 admin-ui/src/components/Crontab/week.vue
create mode 100644 admin-ui/src/components/Crontab/year.vue
create mode 100644 admin-ui/src/components/DictTag/index.vue
create mode 100644 admin-ui/src/components/Editor/index.vue
create mode 100644 admin-ui/src/components/FileUpload/index.vue
create mode 100644 admin-ui/src/components/Hamburger/index.vue
create mode 100644 admin-ui/src/components/HeaderSearch/index.vue
create mode 100644 admin-ui/src/components/IconSelect/index.vue
create mode 100644 admin-ui/src/components/IconSelect/requireIcons.js
create mode 100644 admin-ui/src/components/ImagePreview/index.vue
create mode 100644 admin-ui/src/components/ImageUpload/index.vue
create mode 100644 admin-ui/src/components/Pagination/index.vue
create mode 100644 admin-ui/src/components/ParentView/index.vue
create mode 100644 admin-ui/src/components/RightToolbar/index.vue
create mode 100644 admin-ui/src/components/SvgIcon/index.vue
create mode 100644 admin-ui/src/components/SvgIcon/svgicon.js
create mode 100644 admin-ui/src/components/TopNav/index.vue
create mode 100644 admin-ui/src/components/TreeSelect/index.vue
create mode 100644 admin-ui/src/components/iFrame/index.vue
create mode 100644 admin-ui/src/directive/common/copyText.js
create mode 100644 admin-ui/src/directive/index.js
create mode 100644 admin-ui/src/directive/permission/hasPermi.js
create mode 100644 admin-ui/src/directive/permission/hasRole.js
create mode 100644 admin-ui/src/layout/components/AppMain.vue
create mode 100644 admin-ui/src/layout/components/IframeToggle/index.vue
create mode 100644 admin-ui/src/layout/components/InnerLink/index.vue
create mode 100644 admin-ui/src/layout/components/Navbar.vue
create mode 100644 admin-ui/src/layout/components/Settings/index.vue
create mode 100644 admin-ui/src/layout/components/Sidebar/Link.vue
create mode 100644 admin-ui/src/layout/components/Sidebar/Logo.vue
create mode 100644 admin-ui/src/layout/components/Sidebar/SidebarItem.vue
create mode 100644 admin-ui/src/layout/components/Sidebar/index.vue
create mode 100644 admin-ui/src/layout/components/TagsView/ScrollPane.vue
create mode 100644 admin-ui/src/layout/components/TagsView/index.vue
create mode 100644 admin-ui/src/layout/components/index.js
create mode 100644 admin-ui/src/layout/index.vue
create mode 100644 admin-ui/src/main.js
create mode 100644 admin-ui/src/permission.js
create mode 100644 admin-ui/src/plugins/auth.js
create mode 100644 admin-ui/src/plugins/cache.js
create mode 100644 admin-ui/src/plugins/download.js
create mode 100644 admin-ui/src/plugins/index.js
create mode 100644 admin-ui/src/plugins/modal.js
create mode 100644 admin-ui/src/plugins/tab.js
create mode 100644 admin-ui/src/router/index.js
create mode 100644 admin-ui/src/settings.js
create mode 100644 admin-ui/src/store/index.js
create mode 100644 admin-ui/src/store/modules/app.js
create mode 100644 admin-ui/src/store/modules/dict.js
create mode 100644 admin-ui/src/store/modules/permission.js
create mode 100644 admin-ui/src/store/modules/settings.js
create mode 100644 admin-ui/src/store/modules/tagsView.js
create mode 100644 admin-ui/src/store/modules/user.js
create mode 100644 admin-ui/src/utils/auth.js
create mode 100644 admin-ui/src/utils/dict.js
create mode 100644 admin-ui/src/utils/dynamicTitle.js
create mode 100644 admin-ui/src/utils/errorCode.js
create mode 100644 admin-ui/src/utils/index.js
create mode 100644 admin-ui/src/utils/jsencrypt.js
create mode 100644 admin-ui/src/utils/permission.js
create mode 100644 admin-ui/src/utils/request.js
create mode 100644 admin-ui/src/utils/ruoyi.js
create mode 100644 admin-ui/src/utils/scroll-to.js
create mode 100644 admin-ui/src/utils/theme.js
create mode 100644 admin-ui/src/utils/validate.js
create mode 100644 admin-ui/src/views/error/401.vue
create mode 100644 admin-ui/src/views/error/404.vue
create mode 100644 admin-ui/src/views/index.vue
create mode 100644 admin-ui/src/views/login.vue
create mode 100644 admin-ui/src/views/redirect/index.vue
create mode 100644 admin-ui/src/views/register.vue
create mode 100644 admin-ui/src/views/system/dept/index.vue
create mode 100644 admin-ui/src/views/system/menu/index.vue
create mode 100644 admin-ui/src/views/system/role/authUser.vue
create mode 100644 admin-ui/src/views/system/role/index.vue
create mode 100644 admin-ui/src/views/system/role/selectUser.vue
create mode 100644 admin-ui/src/views/system/user/authRole.vue
create mode 100644 admin-ui/src/views/system/user/index.vue
create mode 100644 admin-ui/src/views/system/user/profile/index.vue
create mode 100644 admin-ui/src/views/system/user/profile/resetPwd.vue
create mode 100644 admin-ui/src/views/system/user/profile/userAvatar.vue
create mode 100644 admin-ui/src/views/system/user/profile/userInfo.vue
create mode 100644 admin-ui/vite.config.js
create mode 100644 admin-ui/vite/plugins/auto-import.js
create mode 100644 admin-ui/vite/plugins/compression.js
create mode 100644 admin-ui/vite/plugins/index.js
create mode 100644 admin-ui/vite/plugins/setup-extend.js
create mode 100644 admin-ui/vite/plugins/svg-icon.js
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6016170
--- /dev/null
+++ b/README.md
@@ -0,0 +1,146 @@
+
django-ruoyi-admin
+基于django+ 若依(Vue3版本)快速开发框架(v1.0.1)
+
+
+ 👉 个人网站:http://124.71.212.219:8028/ 👈
+
+
+
+
+
+
+
+
+
+
+### 平台介绍
+
+若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。[若依框架地址]([Vue3](https://v3.cn.vuejs.org)),
+但目前主流的若以框架是基于SpringBoot的java开发语言,企业开发前端常用的是若依框架,而后端没有一个属于python版本的后端,因此萌生了调试一套适合python程序员的快速开发框架,删除广告页面和日常开发者不必要的功能,只保留项目的最核心的配置模块,减少初始化的方式,做到入手既可以开发功能
+
+python主流框架请查看不同的分支(django+mysql,flask+mongo,sanic-mongo)
+
+
+### 技术栈介绍
+
+* 前端技术栈: [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev)
+若依框架版本。
+* 后端技术栈: [django](https://www.djangoproject.com/) + [mysql](https://www.mysql.com/) + [redis](https://redis.io/) 实现。
+
+
+
+  |
+
+
+
+### 框架能力
+* 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
+* 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
+* 岗位管理:配置系统用户所属担任职务。
+* 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
+
+### 代码优化
+
+* 后端:收集日常的常用方法,增加常用的企业微信机器人通知,数据加解密等常用方法
+* 后端:数据库配置增加nacos和本地config的配置的2种配置,多种配置方式
+* 后端:配置数据库后,项目启动直接初始化相关数据,不需要导入sql的方式,方便快捷开发进度
+* 前端:删除不必要的广告页面,和不必要的功能,比如日志,人员职位,登陆等相关信息,只保留用户,角色,目录,部门核心的模块功能
+* 前端:收集封装日常中大佬们封装的一些方法和看到的一些好的方法,实现工具箱似的开发,开箱即用
+* 前端:优化若依框架的部分ui和提取字段,如title,base_path等变为env配置字段和调试时候的vite代理等均有展示
+
+### 前端部署
+
+```
+# 本地部署
+# 安装依赖(设置镜像源)
+yarn --registry=https://registry.npmmirror.com
+
+# 启动服务
+yarn run dev
+
+# 打包构建
+yarn build:prod
+
+# 前端访问地址
+http://localhost:80
+
+# api地址修改:
+vite.config.js文件 server.proxy.target指向后端地址
+
+=======================================================================
+# 线上docker部署
+# 前提确认宿主机含有node环境,(调试环境) node_version = v21.1.0,
+# 安装了yarn 和vite环境
+# npm install -g yarn vite
+
+# 安装依赖
+yarn --registry=https://registry.npmmirror.com
+
+# 启动服务
+yarn run dev
+
+# 打包构建
+yarn build:prod
+
+# 前端访问地址
+http://localhost:80
+
+# api地址修改:
+.env.production文件 VITE_APP_BASE_API 属性
+
+# 构建命令
+sh build.sh
+```
+
+### 后端部署
+
+```
+本地部署
+# 进入目录
+cd admin-api
+
+# 安装依赖
+pip install -r requirements.txt
+
+# 配置数据库
+/config.py 或者 配置nacos地址 (配置mongo和redis的数据)
+
+# 启动服务
+python mange.py runserver 0.0.0.0:8000
+
+=======================================================================
+# docker部署(docker-compose)
+docker-compose build && docker-compose up -d
+
+# 或者执行脚本构建(gunicorn部署,已内置调试)
+sh build.sh
+
+内置一个超级管理员账号
+superAdmin/superAdmin
+```
+
+### 在线体验
+
+演示地址:[http://124.71.212.219:8101/#/login](http://124.71.212.219:8101/#/login)
+
+账号密码:superAdmin/superAdmin
+
+如果能帮助到您,快速的构建您的项目,麻烦帮我star一下吧,谢谢咯,如果有问题的请加我微信
+
+## 演示图
+
+
+
+  |
+  |
+
+
+  |
+  |
+
+
+  |
+  |
+
+
+
\ No newline at end of file
diff --git a/admin-api/.gitignore b/admin-api/.gitignore
new file mode 100644
index 0000000..79a1148
--- /dev/null
+++ b/admin-api/.gitignore
@@ -0,0 +1,8 @@
+.idea
+/chrome
+/logs
+/meta
+/venv
+/static
+*.pyc
+/excel
\ No newline at end of file
diff --git a/admin-api/Dockerfile b/admin-api/Dockerfile
new file mode 100644
index 0000000..f4cc1ba
--- /dev/null
+++ b/admin-api/Dockerfile
@@ -0,0 +1,27 @@
+FROM python:3.7
+WORKDIR /app
+COPY . /app
+EXPOSE 8085
+
+# 安装依赖
+RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
+RUN python -m pip install --upgrade pip
+RUN python -m pip install gunicorn
+RUN python -m pip install greenlet
+RUN python -m pip install eventlet
+RUN python -m pip install gevent
+RUN python -m pip install -r requirements.txt
+
+# 创建数据
+RUN python createDatabase.py
+RUN python manage.py makemigrations
+RUN python manage.py migrate
+RUN python initDatabase.py
+
+# 修改时区
+RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+
+# gunicorn部署 需要部署nginx 存在跨域问题, 配置文件
+CMD ["gunicorn" ,"application.wsgi", "-c", "gunicorn.conf.py"]
+
+# CMD ["python" ,"manage.py", "runserver", "0.0.0.0:8085"]
diff --git a/admin-api/application/__init__.py b/admin-api/application/__init__.py
new file mode 100644
index 0000000..c45523b
--- /dev/null
+++ b/admin-api/application/__init__.py
@@ -0,0 +1,2 @@
+import pymysql
+pymysql.install_as_MySQLdb()
\ No newline at end of file
diff --git a/admin-api/application/asgi.py b/admin-api/application/asgi.py
new file mode 100644
index 0000000..256c1bc
--- /dev/null
+++ b/admin-api/application/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for application project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
+
+application = get_asgi_application()
diff --git a/admin-api/application/settings.py b/admin-api/application/settings.py
new file mode 100644
index 0000000..5130b36
--- /dev/null
+++ b/admin-api/application/settings.py
@@ -0,0 +1,110 @@
+"""
+Django settings for application project.
+
+Generated by 'django-admin startproject' using Django 3.2.25.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/3.2/ref/settings/
+"""
+
+from pathlib import Path
+
+from config import config
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-68!wtv#sj9)8*eu7gjr(*9m6v3veg#de!e2(51t)04m$%7abhj'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = config.DEBUG
+
+ALLOWED_HOSTS = ["*"]
+
+# Application definition
+
+INSTALLED_APPS = ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages',
+ 'django.contrib.staticfiles', "django_apscheduler", 'corsheaders', "web.apps.WebConfig"
+ "web", ]
+
+MIDDLEWARE = ['django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware',
+ 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'middleware.myMiddleware.AuthMiddleware', 'middleware.myMiddleware.LogMiddleware', ]
+
+ROOT_URLCONF = 'application.urls'
+
+TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': {
+ 'context_processors': ['django.template.context_processors.debug', 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
+
+WSGI_APPLICATION = 'application.wsgi.application'
+
+# Database
+# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
+
+DATABASES = {'default': {'ENGINE': config.MYSQL_ENGINE, 'HOST': config.MYSQL_HOST, # 数据库ip
+ 'PORT': config.MYSQL_PORT, # 数据库端口
+ 'USER': config.MYSQL_USER, # 用户名
+ 'PASSWORD': config.MYSQL_PASSWORD, # 密码
+ 'NAME': config.MYSQL_DB, # 数据库名
+ }}
+
+CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache", "LOCATION": f'redis://:{config.REDIS_PASSWORD or ""}@{config.REDIS_HOST}:6379',
+ "DECODE_RESPONSES": True, "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",
+ "CONNECTION_POOL_KWARGS": {"max_connections": 100, 'decode_responses': True},
+ "PASSWORD": config.REDIS_PASSWORD, }}}
+
+# Password validation
+# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', },
+ {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', },
+ {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', },
+ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]
+
+# Internationalization
+# https://docs.djangoproject.com/en/3.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/3.2/howto/static-files/
+
+STATIC_URL = '/static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+
+# 允许所有域名跨域(优先选择)
+CORS_ORIGIN_ALLOW_ALL = True
+
+# 跨域白名单请求方式
+CORS_ALLOW_METHODS = ('DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW',)
+
+# 跨域白名单请求头
+CORS_ALL_HEADERS = ('XMLHttpRequest', 'accept-encoding', 'authorization', 'content-type', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with')
+
+# 关闭浏览器退出登录
+SESSION_EXPIRE_AT_BROWSER_CLOSE = True
+
+# 解决浏览器不能设置cookie问题
+SESSION_COOKIE_SAMESITE = None
+X_FRAME_OPTIONS = 'ALLOWALL'
diff --git a/admin-api/application/urls.py b/admin-api/application/urls.py
new file mode 100644
index 0000000..c768fed
--- /dev/null
+++ b/admin-api/application/urls.py
@@ -0,0 +1,21 @@
+"""application URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.urls import path, include
+
+urlpatterns = [
+ path('api/', include('web.urls')),
+ path('api/system/', include('web.system.urls')),
+]
diff --git a/admin-api/application/wsgi.py b/admin-api/application/wsgi.py
new file mode 100644
index 0000000..89d4bec
--- /dev/null
+++ b/admin-api/application/wsgi.py
@@ -0,0 +1,15 @@
+"""
+WSGI config for application project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
+"""
+
+import os
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
+
+application = get_wsgi_application()
diff --git a/admin-api/build.sh b/admin-api/build.sh
new file mode 100644
index 0000000..4238d63
--- /dev/null
+++ b/admin-api/build.sh
@@ -0,0 +1,3 @@
+git pull
+docker-compose build
+docker-compose up -d
\ No newline at end of file
diff --git a/admin-api/config.py b/admin-api/config.py
new file mode 100644
index 0000000..4dbc354
--- /dev/null
+++ b/admin-api/config.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+@Author:mengying
+@file: config.py
+@date:2023/12/18 9:35
+@email: 652044581@qq.com
+@desc:
+"""
+import nacos
+from addict import Dict
+import json
+
+
+class Localconfig:
+ """本地配置(二选一)"""
+
+ # 项目的名称
+ PROJECT_NAME = "django-ruoyi-admin"
+
+ # 配置redis缓存地址
+ REDIS_HOST = '124.71.212.219'
+ REDIS_PORT = 6379
+ REDIS_DB = 0
+ REDIS_PASSWORD = "Zds2Xacb0kbiSGcs"
+
+ # 配置mongo数据库
+ MYSQL_DB = "django_ruoyi_admin" # 注: 数据库名不能用-特殊字符
+ MYSQL_HOST = "124.71.212.219"
+ MYSQL_PORT = 3306
+ MYSQL_USER = "root"
+ MYSQL_PASSWORD = "tpZCI6IiJ9"
+ MYSQL_ENGINE = 'django.db.backends.mysql'
+
+ # 加密随机串(hash-md5)
+ ENCRYPT_STRING = "c-QULHn+u=-BUSQ$"
+
+ DEBUG = False
+
+ @classmethod
+ def get_server_config(cls):
+ return {item: getattr(cls, item) for item in dir(cls)}
+
+
+class NacosClient:
+ """nacos配置(二选一)"""
+
+ def __init__(self, addr: str = None, namespace: str = None, data_id: str = None, group_id: str = None,
+ username: str = "nacos", password: str = "nacos"):
+ self.addr = addr or "120.46.187.114:8848"
+ self.namespace = namespace or "7d35aedc-ec57-48c8-8489-9974d19a2942"
+ self.data_id = data_id or "sigin"
+ self.group_id = group_id or "dev"
+ self.client = nacos.NacosClient(self.addr, namespace=self.namespace, username=username, password=password)
+
+ def get_server_config(self):
+ print(self.client.get_config(self.data_id, self.group_id))
+ return json.loads(self.client.get_config(self.data_id, self.group_id))
+
+
+# nacos的配置
+# config = Dict(NacosClient().get_server_config())
+
+# 本地的配置
+config = Dict(Localconfig.get_server_config())
+
+
+if __name__ == '__main__':
+ print(config.MYSQL_DB)
diff --git a/admin-api/createDatabase.py b/admin-api/createDatabase.py
new file mode 100644
index 0000000..4456064
--- /dev/null
+++ b/admin-api/createDatabase.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+"""
+@Author :mengying
+@Date :2024/5/31 17:31
+@Email : 652044581@qq.com
+@Desc : 创建数据库
+"""
+import pymysql
+
+from config import config
+
+
+def create_database():
+ """项目启动自动创建数据表"""
+ connection = pymysql.connect(host=config.MYSQL_HOST, port=config.MYSQL_PORT, user=config.MYSQL_USER, passwd=config.MYSQL_PASSWORD)
+ create_database_sql = f"CREATE DATABASE IF NOT EXISTS {config.MYSQL_DB} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
+ try:
+ # 创建游标对象
+ with connection.cursor() as cursor:
+ # 执行SQL语句
+ cursor.execute(create_database_sql)
+ # 提交事务
+ connection.commit()
+ except pymysql.MySQLError as e:
+ print(f"Error: {e}")
+ finally:
+ # 关闭数据库连接
+ connection.close()
+
+
+# 初始化创建数据库
+create_database()
diff --git a/admin-api/crontab/myScheduler.py b/admin-api/crontab/myScheduler.py
new file mode 100644
index 0000000..c155f2f
--- /dev/null
+++ b/admin-api/crontab/myScheduler.py
@@ -0,0 +1,20 @@
+from apscheduler.schedulers.background import BackgroundScheduler
+from django_apscheduler.jobstores import DjangoJobStore, register_events, register_job
+import uuid
+
+import datetime
+
+scheduler = BackgroundScheduler()
+scheduler.add_jobstore(DjangoJobStore(), "default")
+
+run_date = datetime.datetime.now() + datetime.timedelta(seconds=20)
+
+
+@register_job(scheduler, "date", id=uuid.uuid4().hex, run_date=run_date, replace_existing=True,
+ timezone='Asia/Shanghai')
+def setUp_database_scheduler():
+ print("Setting up database scheduler")
+
+
+register_events(scheduler)
+scheduler.start()
diff --git a/admin-api/database/core.py b/admin-api/database/core.py
new file mode 100644
index 0000000..e7205e8
--- /dev/null
+++ b/admin-api/database/core.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+"""
+@Author :mengying
+@Date :2024/5/30 15:39
+@Email : 652044581@qq.com
+@Desc : 功能描述
+"""
+from django.db import models
+
+
+class CoreModel(models.Model):
+ """
+ 核心标准抽象模型
+ """
+ id = models.BigAutoField(primary_key=True, help_text="Id", verbose_name="Id")
+ update_time = models.DateTimeField(auto_now=True, null=True, blank=True, help_text="修改时间", verbose_name="修改时间")
+ create_time = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间", verbose_name="创建时间")
+ remark = models.CharField(max_length=64, blank=True, verbose_name="备注信息", help_text="备注信息")
+ createBy = models.CharField(max_length=64, blank=True, verbose_name="创建者", help_text="创建者")
+ objects = models.Manager()
+
+ class Meta:
+ abstract = True
+ verbose_name = '核心模型'
+ verbose_name_plural = verbose_name
diff --git a/admin-api/docker-compose.yaml b/admin-api/docker-compose.yaml
new file mode 100644
index 0000000..b6403f1
--- /dev/null
+++ b/admin-api/docker-compose.yaml
@@ -0,0 +1,21 @@
+version: '3.0'
+
+services:
+
+ django-vue-api-std:
+
+ build:
+ context: .
+ dockerfile: Dockerfile
+
+ image: django-vue-api-image-std
+
+ container_name: django-vue-api-container-std
+
+ volumes:
+ - /opt/django-vue-admin/logs:/app/logs
+
+ ports:
+ - "8090:8085"
+
+ restart: always
\ No newline at end of file
diff --git a/admin-api/gunicorn.conf.py b/admin-api/gunicorn.conf.py
new file mode 100644
index 0000000..a83b460
--- /dev/null
+++ b/admin-api/gunicorn.conf.py
@@ -0,0 +1,42 @@
+# 多进程
+
+"""gunicorn+gevent 的配置文件"""
+
+timeout = 60 * 2
+
+# 预加载资源
+# preload_app = True
+# 绑定 ip + 端口
+bind = "0.0.0.0:8085"
+# 进程数 = cup数量 * 2 + 1
+workers = 4 # multiprocessing.cpu_count() * 2 + 1
+
+# 线程数 = cup数量 * 2
+threads = 20
+
+max_requests = 1000
+
+# 等待队列最大长度,超过这个长度的链接将被拒绝连接
+backlog = 2048
+
+# 工作模式--协程
+worker_class = "gevent"
+
+# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响
+# 服务器配置设置的值 1200:中小型项目 上万并发: 中大型
+# 服务器硬件:宽带+数据库+内存
+# 服务器的架构:集群 主从
+worker_connections = 1200
+
+# 进程名称
+proc_name = 'w2p.pid'
+# 进程pid记录文件
+pidfile = 'app_run.log'
+# 日志等级
+loglevel = 'debug'
+# 日志文件名
+logfile = 'debug.log'
+# 访问记录
+accesslog = 'access.log'
+# 访问记录格式
+access_log_format = '%(h)s %(t)s %(U)s %(q)s'
diff --git a/admin-api/initDatabase.py b/admin-api/initDatabase.py
new file mode 100644
index 0000000..d370d4d
--- /dev/null
+++ b/admin-api/initDatabase.py
@@ -0,0 +1,386 @@
+# -*- coding: utf-8 -*-
+"""
+@Author :mengying
+@Date :2024/5/31 17:17
+@Email : 652044581@qq.com
+@Desc : 初始化数据
+"""
+import os, django
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
+django.setup()
+
+import logging
+from utils.mySnowflake import Sf
+from web.models import SystemInit
+from web.models import SystemDept, SystemRole, SystemUser, SystemMenu, SystemUserRole
+from django_redis import get_redis_connection
+from utils.myEnum import SystemUserTypeEnum
+from config import config
+from utils.myEncrypt import HashCipher
+
+from redis import Redis
+
+RedisClient: Redis = get_redis_connection()
+logger = logging.getLogger(__file__)
+
+
+class DatabaseUtils(object):
+ REDIS_KEY = 'init_database'
+
+ @classmethod
+ def init_database(cls):
+ """初始化数据库"""
+
+ # 多节点初始化
+ if not RedisClient.setnx(cls.REDIS_KEY, cls.REDIS_KEY):
+ return
+
+ RedisClient.expire(cls.REDIS_KEY, 10)
+ hasInit: bool = cls.initFlag()
+ deptId = Sf.generate()
+ roleId = Sf.generate()
+ userId = Sf.generate()
+
+ if not hasInit:
+ cls.initDept(deptId)
+ cls.initRole(roleId)
+ cls.initUser(userId)
+ cls.initUserRole(userId, roleId)
+ cls.initMenu()
+
+ RedisClient.delete(cls.REDIS_KEY)
+ logger.info("init database successfully")
+
+ @classmethod
+ def initDept(cls, deptId):
+ initDeptData = {
+ "createBy": "superAdmin",
+ "deptId": deptId,
+ "ancestors": "0",
+ "parentId": "0",
+ "deptName": "宇驰商贸有限公司",
+ "orderNum": 0,
+ "leader": "admin",
+ "phone": "17783098377",
+ "email": "652044581@qq.com",
+ "status": "0",
+ "delFlag": "0",
+ }
+ SystemDept(**initDeptData).save()
+
+ @classmethod
+ def initRole(cls, roleId):
+ """初始化角色超级管理员"""
+ initRoleData = {
+ "createBy": "superAdmin",
+ "roleId": roleId,
+ "roleName": "超级管理员",
+ "roleAdmin": True,
+ "roleKey": "superAdmin",
+ "roleSort": "0",
+ "status": "0",
+ "delFlag": "0",
+ }
+ SystemRole(**initRoleData).save()
+
+ @classmethod
+ def initUser(cls, userId):
+ initUserData = {"userId": userId,
+ "username": "superAdmin",
+ "nickName": "superAdmin",
+ "password": HashCipher.md5(config.ENCRYPT_STRING + "superAdmin"),
+ "phone": "17783098375",
+ "email": "652044581@qq.com",
+ "status": "0",
+ "userType": SystemUserTypeEnum.p1.value
+ }
+ SystemUser(**initUserData).save()
+
+ @classmethod
+ def initUserRole(cls, userId, roleId):
+ """初始化用户角色关系"""
+ initUserRoleData = {"userId": userId, "roleId": roleId}
+ SystemUserRole(**initUserRoleData).save()
+
+ @classmethod
+ def initMenu(cls, ):
+ """初始化前端菜单"""
+ menuParentId = Sf.generate()
+ menuUserId = Sf.generate()
+ menuRoleId = Sf.generate()
+ menuMenuId = Sf.generate()
+ menuDeptId = Sf.generate()
+ initMenuData = [
+ {
+ "menuId": menuParentId,
+ "menuName": "系统管理",
+ "parentId": "0",
+ "orderNum": 3,
+ "path": "system",
+ "component": "",
+ "perms": "",
+ "query": "",
+ "menuType": "M",
+ "icon": "system",
+ },
+ {
+ "menuId": menuUserId,
+ "menuName": "用户管理",
+ "parentId": menuParentId,
+ "orderNum": 1,
+ "path": "user",
+ "component": "system/user/index",
+ "perms": "system:user:list",
+ "query": "",
+ "menuType": "C",
+ "icon": "user",
+ },
+
+ {
+ "menuId": menuRoleId,
+ "menuName": "角色管理",
+ "parentId": menuParentId,
+ "orderNum": 2,
+ "path": "role",
+ "component": "system/role/index",
+ "perms": "system:role:list",
+ "query": "",
+ "menuType": "C",
+ "icon": "peoples",
+ },
+ {
+ "menuId": menuMenuId,
+ "menuName": "菜单管理",
+ "parentId": menuParentId,
+ "orderNum": 3,
+ "path": "menu",
+ "component": "system/menu/index",
+ "perms": "system:menu:list",
+ "query": "",
+ "menuType": "C",
+ "icon": "tree-table",
+ },
+ {
+ "menuId": menuDeptId,
+ "menuName": "部门管理",
+ "parentId": menuParentId,
+ "orderNum": 4,
+ "path": "dept",
+ "component": "system/dept/index",
+ "perms": "system:dept:list",
+ "query": "",
+ "menuType": "C",
+ "icon": "tree",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "用户查询",
+ "parentId": menuUserId,
+ "orderNum": 1,
+ "path": "",
+ "component": "",
+ "perms": "system:user:query",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "用户新增",
+ "parentId": menuUserId,
+ "orderNum": 2,
+ "path": "",
+ "component": "",
+ "perms": "system:user:add",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "用户修改",
+ "parentId": menuUserId,
+ "orderNum": 3,
+ "path": "",
+ "component": "",
+ "perms": "system:user:edit",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "用户删除",
+ "parentId": menuUserId,
+ "orderNum": 4,
+ "path": "",
+ "component": "",
+ "perms": "system:user:remove",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+
+ {
+ "menuId": Sf.generate(),
+ "menuName": "角色查询",
+ "parentId": menuRoleId,
+ "orderNum": 1,
+ "path": "",
+ "component": "",
+ "perms": "system:role:query",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "角色新增",
+ "parentId": menuRoleId,
+ "orderNum": 2,
+ "path": "",
+ "component": "",
+ "perms": "system:role:add",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "角色修改",
+ "parentId": menuRoleId,
+ "orderNum": 3,
+ "path": "",
+ "component": "",
+ "perms": "system:role:edit",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "角色删除",
+ "parentId": menuRoleId,
+ "orderNum": 4,
+ "path": "",
+ "component": "",
+ "perms": "system:role:remove",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+
+ {
+ "menuId": Sf.generate(),
+ "menuName": "菜单查询",
+ "parentId": menuMenuId,
+ "orderNum": 1,
+ "path": "",
+ "component": "",
+ "perms": "system:menu:query",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "菜单新增",
+ "parentId": menuMenuId,
+ "orderNum": 2,
+ "path": "",
+ "component": "",
+ "perms": "system:menu:add",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "菜单修改",
+ "parentId": menuMenuId,
+ "orderNum": 3,
+ "path": "",
+ "component": "",
+ "perms": "system:menu:edit",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "菜单删除",
+ "parentId": menuMenuId,
+ "orderNum": 4,
+ "path": "",
+ "component": "",
+ "perms": "system:menu:remove",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+
+ {
+ "menuId": Sf.generate(),
+ "menuName": "部门查询",
+ "parentId": menuDeptId,
+ "orderNum": 1,
+ "path": "",
+ "component": "",
+ "perms": "system:dept:query",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "部门新增",
+ "parentId": menuDeptId,
+ "orderNum": 2,
+ "path": "",
+ "component": "",
+ "perms": "system:dept:add",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "部门修改",
+ "parentId": menuDeptId,
+ "orderNum": 3,
+ "path": "",
+ "component": "",
+ "perms": "system:dept:edit",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ {
+ "menuId": Sf.generate(),
+ "menuName": "部门删除",
+ "parentId": menuDeptId,
+ "orderNum": 4,
+ "path": "",
+ "component": "",
+ "perms": "system:dept:remove",
+ "query": "",
+ "menuType": "F",
+ "icon": "#",
+ },
+ ]
+ for item in initMenuData:
+ SystemMenu(**item).save()
+
+ @classmethod
+ def initFlag(cls):
+ """防止多次初始化文件"""
+ init = SystemInit.objects.all()
+ if not init:
+ SystemInit(Init=True).save()
+ return False
+ else:
+ return True
+
+
+DatabaseUtils.init_database()
diff --git a/admin-api/manage.py b/admin-api/manage.py
new file mode 100644
index 0000000..a773519
--- /dev/null
+++ b/admin-api/manage.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError("Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?") from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/admin-api/middleware/myAuthorization.py b/admin-api/middleware/myAuthorization.py
new file mode 100644
index 0000000..d576617
--- /dev/null
+++ b/admin-api/middleware/myAuthorization.py
@@ -0,0 +1,50 @@
+# -*- coding: -*-
+"""
+@Author:mengying
+@file: myAuthorization.py
+@date:2023/6/14 16:46
+@email: 652044581@qq.com
+@desc: 授权相关
+"""
+import json
+
+from django_redis import get_redis_connection
+
+
+class CacheKeys:
+ TOKEN_NAME = "Authorization"
+
+
+class Authorization:
+
+ @staticmethod
+ def get_user_info(request):
+ """从header中获取访问人员信息放在header中"""
+ RedisClient = get_redis_connection()
+
+ token = request.headers.get(CacheKeys.TOKEN_NAME, '')
+
+ if not token:
+ setattr(request.headers, "user_info", {})
+ return None
+
+ user = RedisClient.get(token)
+ RedisClient.expire(token, 30 * 60)
+
+ if user:
+ user = json.loads(user)
+ user["token"] = token
+ else:
+ user = {}
+
+ setattr(request.headers, "user_info", user)
+
+ @staticmethod
+ def white_list_check(request):
+ """判断访问名路径是否是白名单"""
+
+ white_list = ['/api/login', '/api/captchaImage', '/api/logout']
+ if request.path in white_list:
+ return True
+ else:
+ return False
diff --git a/admin-api/middleware/myMiddleware.py b/admin-api/middleware/myMiddleware.py
new file mode 100644
index 0000000..3038700
--- /dev/null
+++ b/admin-api/middleware/myMiddleware.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+"""
+@Author :mengying
+@Date :2024/5/30 14:16
+@Email : 652044581@qq.com
+@Desc : 功能描述
+"""
+import logging
+import time
+import traceback
+import uuid
+
+from django.http.response import JsonResponse
+
+from middleware.myAuthorization import Authorization
+from utils.myResFormat import ResultJson, ResultCode
+
+logger = logging.getLogger(__file__)
+
+from django.utils.deprecation import MiddlewareMixin
+
+
+class AuthMiddleware(MiddlewareMixin):
+ """用户认证中间件"""
+
+ def process_request(self, request):
+ # 获取用户信息
+ Authorization.get_user_info(request)
+
+ # 检查白名单
+ if not Authorization.white_list_check(request):
+ user_info = getattr(request.headers, 'user_info', {})
+ if not user_info:
+ return JsonResponse(ResultJson(ResultCode.TOKEN_ERROR).result)
+
+ def process_response(self, request, response): # 基于请求响应
+ return response
+
+ def process_exception(self, request, exception):
+
+ formatter = ["接口地址 : %s" % request.get_full_path(), "请求方式 : %s" % request.method, "请求参数 : body: %s" % request.body.decode(),
+ "用户信息 : user: %s" % str(getattr(request.headers, 'user_info', {})), "报错信息 : %s" % str(traceback.format_exc())]
+ error_message = "\n".join(formatter)
+ logger.error(error_message)
+ return JsonResponse(ResultJson(ResultCode.SERVER_ERROR, description=str(exception)).result)
+
+
+class LogMiddleware(MiddlewareMixin):
+ """记录日志中间件"""
+
+ def process_request(self, request):
+ uid = uuid.uuid4().hex
+ setattr(request.headers, "my-duration", time.time())
+ setattr(request.headers, "uid", uid)
+ self.recordsRequest(uid, request.get_full_path(), request.method, request.body.decode())
+
+ def process_response(self, request, response): # 基于请求响应
+ duration = time.time() - getattr(request.headers, "my-duration")
+ uid = getattr(request.headers, "uid")
+ self.recordsResponse(uid, request.get_full_path(), response.content.decode(), duration)
+ return response
+
+ @classmethod
+ def white_path(cls, path):
+ request_white_path = []
+ return path in request_white_path
+
+ @classmethod
+ def recordsRequest(cls, uid, path, method, data):
+ request_info = '链路id: %s 接口: %s 请求方式: %s body参数: %s' % (uid, path, method, data)
+ if cls.white_path(path):
+ return
+ logger.info(str(request_info))
+
+ @classmethod
+ def recordsResponse(cls, uid, path, data, duration=None):
+ response_info = '链路id: %s 接口: %s 返回数据: %s 耗时: %s ' % (uid, path, data, duration)
+ if cls.white_path(path):
+ return
+ logger.info(str(response_info))
diff --git a/admin-api/requirements.txt b/admin-api/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c80be7835eeea9dcef91308ddde896c8bfa1bcdf
GIT binary patch
literal 994
zcma)*O;5sL5QO(^;!lCtA{q}KOgtG)Fqn8We9D)l@?!Y-@#-_XLLquEO)0Rmvoo`A
zzdk$G=NenjmUhjRTiwpN&+ODr?8aKwrZ%zxs1KX*TsW=)CIcn3kR40*3eN(>aF4j8
zp6}}$E8wazX>4!c<}k+8m{_-lW4o~RY-%Nl1&9R8mhS~UV?Kv`msU-e-psphZ9-px
zil|DVs<}12-an|-X~wjT|HNn@SsGLo$^SoMjkTmNba~#sVL<@HbGSx
zA0mZII3B{<@r+hJak@`5!_Cg||2k{Gl)Te2&SJfW4
z+xBSp`+9I|Los>ll^nj@vyI;0xmPDPp=f=;Cy${f**i*0Txz!i$3)-mv@g+9(y!C@
z0%DE#0l&nl1gaBJ`B-v>`*14Tc+-xxs;+!`)Mt*UHob+n MAX_WORKER_ID or worker_id < 0:
+ raise ValueError('worker_id 值越界')
+ if datacenter_id > MAX_DATACENTER_ID or datacenter_id < 0:
+ raise ValueError('datacenter_id 值越界')
+
+ self.worker_id = worker_id
+ self.datacenter_id = datacenter_id
+ self.sequence = sequence
+
+ self.last_timestamp = -1 # 上次计算的时间戳
+
+ def _gen_timestamp(self):
+ """
+ 生成整数时间戳。
+ :return:
+ """
+ return int(time.time() * 1000)
+
+ def generate(self) -> str:
+ """
+ 获取新的ID.
+ :return:
+ """
+ # 获取当前时间戳
+ timestamp = self._gen_timestamp()
+
+ # 时钟回拨的情况
+ if timestamp < self.last_timestamp:
+ raise Exception("clock is moving backwards")
+
+ if timestamp == self.last_timestamp:
+ # 同一毫秒的处理。
+ self.sequence = (self.sequence + 1) & SEQUENCE_MASK
+ if self.sequence == 0:
+ timestamp = self._til_next_millis(self.last_timestamp)
+ else:
+ self.sequence = 0
+
+ self.last_timestamp = timestamp
+
+ new_id = (((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (self.datacenter_id << DATACENTER_ID_SHIFT) | (
+ self.worker_id << WORKER_ID_SHIFT)) | self.sequence
+ return str(new_id)
+
+ def _til_next_millis(self, last_timestamp):
+ """
+ 等到下一毫秒。
+ :param last_timestamp:
+ :return:
+ """
+ timestamp = self._gen_timestamp()
+ while timestamp <= last_timestamp:
+ timestamp = self._gen_timestamp()
+ return timestamp
+
+
+Sf = SnowIdWorker()
+
+if __name__ == '__main__':
+ for i in range(1000):
+ print(Sf.generate())
diff --git a/admin-api/utils/myTimeFormat.py b/admin-api/utils/myTimeFormat.py
new file mode 100644
index 0000000..6e6c2f4
--- /dev/null
+++ b/admin-api/utils/myTimeFormat.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+"""
+@Author: 孟颖
+@email: 652044581@qq.com
+@date: 2023/4/23 10:03
+@desc: 时间类,生成各类的时间和时间格式转换
+"""
+import time
+from datetime import datetime, timedelta
+
+
+class MyTimeUtils:
+ dateTimeType = '%Y-%m-%d %H:%M:%S'
+ dateType = '%Y-%m-%d'
+ timeType = '%H:%M:%S'
+ fileTimeType = "%Y%m%d%H%M%S%f"
+ fileTimeShortType = "%Y%m%d%H%M%S"
+
+ @classmethod
+ def TimeFormat(cls, now_time: datetime = datetime.now(), timeType: str = dateTimeType) -> str:
+ return now_time.strftime(timeType)
+
+ @classmethod
+ def TimeFormatCh(cls, now_time: datetime = datetime.now()) -> str:
+ ch_time = now_time.strftime(cls.dateType)
+ return "%s年%s月%s日" % tuple(ch_time.split("-"))
+
+ @classmethod
+ def TimestampFormat(cls, long: bool = False) -> int:
+ return int(time.time() * 1000) if long else int(time.time())
+
+ @classmethod
+ def TimeOffsetFormat(cls, start_time: datetime = datetime.now(), days: int = 0, hour: int = 0, minute: int = 0, second: int = 0,
+ timeType: str = dateTimeType) -> str:
+ offsetDateTime = start_time + timedelta(days=days, hours=hour, minutes=minute, seconds=second)
+ return cls.TimeFormat(offsetDateTime, timeType)
+
+
+if __name__ == '__main__':
+ print(MyTimeUtils.TimeOffsetFormat(days=-1, timeType=MyTimeUtils.fileTimeType))
+ print(MyTimeUtils.TimeFormatCh())
diff --git a/admin-api/web/__init__.py b/admin-api/web/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/admin-api/web/admin.py b/admin-api/web/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/admin-api/web/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/admin-api/web/apps.py b/admin-api/web/apps.py
new file mode 100644
index 0000000..fa72239
--- /dev/null
+++ b/admin-api/web/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class WebConfigweb(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'web'
diff --git a/admin-api/web/captchaImage.py b/admin-api/web/captchaImage.py
new file mode 100644
index 0000000..246ef40
--- /dev/null
+++ b/admin-api/web/captchaImage.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+"""
+@Author: 孟颖
+@email: 652044581@qq.com
+@date: 2023/4/13 13:36
+@desc: 生成验证码
+"""
+import pathlib
+import random
+from PIL import Image, ImageDraw, ImageFont
+from io import BytesIO
+import base64
+from pathlib import Path
+
+
+class CaptchaImage:
+
+ def __init__(self, width=130, height=35, string_count=4, font_size=30, noise_line=3, noise_point=30):
+ """
+ :param width: 图片宽度
+ :param height: 图片高度
+ :param string_count: 验证码的数量
+ :param font_size: 字体的大小
+ :param noise_line: 噪声线的数量
+ :param noise_point: 噪声点的数量
+ """
+ self.width = width
+ self.height = height
+ self.string_count = string_count
+ self.font_size = font_size
+ self.noise_line = noise_line
+ self.noise_point = noise_point
+
+ # 生成验证码
+ def generate(self, width=120, height=35):
+ image = self.background()
+ captcha_string, image = self.draw_string(image)
+ image: Image = self.noise(image)
+ image = image.resize((width, height), resample=Image.BICUBIC)
+ output_buffer = BytesIO()
+ image.save(output_buffer, format='png')
+ binary_data = output_buffer.getvalue()
+ image_base64 = self.io2base64(binary_data)
+ return captcha_string, image_base64
+
+ # io转base64
+ def io2base64(self, content: bytes):
+ encode_data = base64.b64encode(content)
+ return str(encode_data, encoding='utf-8')
+
+ # 生成背景
+ def background(self):
+ background_color = (255, 255, 255)
+ image = Image.new('RGB', (self.width, self.height), background_color)
+ return image
+
+ # 随机颜色
+ def random_color(self):
+ c1 = random.randint(0, 200)
+ c2 = random.randint(0, 200)
+ c3 = random.randint(0, 200)
+ return c1, c2, c3
+
+ # 生成随机字符串
+ def random_string(self):
+ random_num = str(random.randint(0, 9))
+ random_low_alpha = chr(random.randint(97, 122))
+ return random.choice([random_num, random_low_alpha])
+
+ # 合成文字
+ def draw_string(self, image: Image):
+ draw = ImageDraw.Draw(image)
+ font_file = pathlib.Path.joinpath(Path(__file__).parent, "static/fontBold.ttf").as_posix()
+ font = ImageFont.truetype(font=font_file, size=self.font_size)
+ string_container = []
+ for i in range(self.string_count):
+ random_char = self.random_string()
+ draw.text((10 + i * 30, -2), random_char, self.random_color(), font=font)
+ string_container.append(random_char)
+ captcha_string = "".join(string_container)
+ return captcha_string, image
+
+ # 生成噪声
+ def noise(self, image: Image):
+
+ draw = ImageDraw.Draw(image)
+ # 噪声线
+ for i in range(self.noise_line):
+ x1 = random.randint(0, self.width)
+ x2 = random.randint(0, self.width)
+ y1 = random.randint(0, self.height)
+ y2 = random.randint(0, self.height)
+ draw.line((x1, y1, x2, y2), fill=self.random_color())
+
+ # 噪声点
+ for i in range(self.noise_point):
+ draw.point([random.randint(0, self.width), random.randint(0, self.height)], fill=self.random_color())
+ x = random.randint(0, self.width)
+ y = random.randint(0, self.height)
+ draw.arc((x, y, x + 4, y + 4), 0, 90, fill=self.random_color())
+
+ return image
+
+
+if __name__ == '__main__':
+ captcha = CaptchaImage()
+
+ # 生成验证码图片和字符串
+ captcha_string, image_base64 = captcha.generate(width=108, height=36)
diff --git a/admin-api/web/migrations/0001_initial.py b/admin-api/web/migrations/0001_initial.py
new file mode 100644
index 0000000..fd35f32
--- /dev/null
+++ b/admin-api/web/migrations/0001_initial.py
@@ -0,0 +1,37 @@
+# Generated by Django 3.2.25 on 2024-05-31 04:06
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SystemDept',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('parentId', models.CharField(default='', help_text='父id', max_length=64, verbose_name='父id')),
+ ('deptName', models.CharField(default='', help_text='部门名称', max_length=64, verbose_name='部门名称')),
+ ('deptId', models.CharField(default='', help_text='部门id', max_length=64, unique=True, verbose_name='部门id')),
+ ('orderNum', models.IntegerField(default=0, help_text='排序', verbose_name='排序')),
+ ('leader', models.CharField(default='', help_text='负责人', max_length=64, verbose_name='负责人')),
+ ('phone', models.CharField(default='', help_text='联系电话', max_length=64, verbose_name='联系电话')),
+ ('email', models.CharField(default='', help_text='邮箱', max_length=64, verbose_name='邮箱')),
+ ('status', models.CharField(default='', help_text='启用状态 (0正常 1停用)', max_length=64, verbose_name='启用状态')),
+ ('delFlag', models.CharField(default='', help_text='是否删除 (0存在 2删除)', max_length=64, verbose_name='是否删除')),
+ ('ancestors', models.CharField(default='', help_text='上级目录id', max_length=64, verbose_name='上级目录id')),
+ ('createBy', models.CharField(default='', help_text='创建者', max_length=64, verbose_name='创建者')),
+ ],
+ options={
+ 'verbose_name': '部门管理',
+ 'db_table': 'system_dept',
+ },
+ ),
+ ]
diff --git a/admin-api/web/migrations/0002_auto_20240531_1456.py b/admin-api/web/migrations/0002_auto_20240531_1456.py
new file mode 100644
index 0000000..d62a8f9
--- /dev/null
+++ b/admin-api/web/migrations/0002_auto_20240531_1456.py
@@ -0,0 +1,174 @@
+# Generated by Django 3.2.25 on 2024-05-31 06:56
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('web', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SystemInit',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
+ ('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
+ ('Init', models.BooleanField(blank=True, default=False, help_text='是否已经初始化数据库')),
+ ],
+ options={
+ 'verbose_name': '核心模型',
+ 'verbose_name_plural': '核心模型',
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='SystemMenu',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
+ ('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
+ ('menuId', models.CharField(default='', help_text='菜单ID', max_length=64, unique=True, verbose_name='菜单ID')),
+ ('menuName', models.CharField(default='', help_text='菜单名称', max_length=64, verbose_name='菜单名称')),
+ ('parentId', models.CharField(default='', help_text='父菜单ID', max_length=64, verbose_name='父菜单ID')),
+ ('orderNum', models.IntegerField(default=0, help_text='显示顺序', verbose_name='显示顺序')),
+ ('path', models.CharField(default='', help_text='路由地址', max_length=64, verbose_name='路由地址')),
+ ('component', models.CharField(default='', help_text='组件路径', max_length=64, verbose_name='组件路径')),
+ ('query', models.CharField(default='', help_text='路由参数', max_length=64, verbose_name='路由参数')),
+ ('isFrame', models.CharField(default='', help_text='是否为外链(0是 1否)', max_length=4, verbose_name='是否为外链(0是 1否)')),
+ ('isCache', models.CharField(default='', help_text='是否缓存(0缓存 1不缓存)', max_length=4, verbose_name='是否缓存(0缓存 1不缓存)')),
+ ('visible', models.CharField(default='', help_text='菜单状态(0显示 1隐藏)', max_length=4, verbose_name='菜单状态(0显示 1隐藏)')),
+ ('menuType', models.CharField(default='', help_text='菜单类型(M目录 C菜单 F按钮)', max_length=4, verbose_name='菜单类型(M目录 C菜单 F按钮)')),
+ ('status', models.CharField(default='', help_text='菜单状态(0正常 1停用)', max_length=4, verbose_name='菜单状态(0正常 1停用)')),
+ ('perms', models.CharField(default='', help_text='权限标识', max_length=32, verbose_name='权限标识')),
+ ('icon', models.CharField(default='', help_text='菜单图', max_length=32, verbose_name='菜单图')),
+ ],
+ options={
+ 'verbose_name': '目录管理',
+ 'db_table': 'system_menu',
+ },
+ ),
+ migrations.CreateModel(
+ name='SystemRole',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
+ ('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
+ ('roleId', models.CharField(default='', help_text='角色id', max_length=64, unique=True, verbose_name='角色id')),
+ ('roleName', models.CharField(default='', help_text='角色名称', max_length=64, verbose_name='角色名称')),
+ ('roleKey', models.CharField(default='', help_text='角色权限字符', max_length=64, verbose_name='角色权限字符')),
+ ('roleSort', models.IntegerField(default=0, help_text='角色排序', verbose_name='角色排序')),
+ ('roleAdmin', models.BooleanField(default=False, help_text='是否是超级管理员', max_length=64, verbose_name='是否是超级管理员')),
+ ('status', models.CharField(default='0', help_text='启用状态 (0正常 1停用)', max_length=4, verbose_name='启用状态 (0正常 1停用)')),
+ ('menuCheckStrictly', models.BooleanField(default=True, help_text='菜单树选择项是否关联显示', verbose_name='菜单树选择项是否关联显示')),
+ ('deptCheckStrictly', models.BooleanField(default=True, help_text='部门树选择项是否关联显示', verbose_name='部门树选择项是否关联显示')),
+ ('delFlag', models.CharField(default='0', help_text='是否删除 (0代表存在 2代表删除)', max_length=4, verbose_name='是否删除 (0代表存在 2代表删除)')),
+ ],
+ options={
+ 'verbose_name': '角色管理',
+ 'db_table': 'system_role',
+ },
+ ),
+ migrations.CreateModel(
+ name='SystemRoleMenu',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
+ ('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
+ ('menuId', models.CharField(default='', help_text='菜单id', max_length=64, verbose_name='菜单id')),
+ ('roleId', models.CharField(default='', help_text='角色id', max_length=64, verbose_name='角色id')),
+ ],
+ options={
+ 'verbose_name': '角色与目录关系映射',
+ 'db_table': 'system_role_menu',
+ },
+ ),
+ migrations.CreateModel(
+ name='SystemUser',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
+ ('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
+ ('userId', models.CharField(default='', help_text='用户id', max_length=64, unique=True, verbose_name='用户id')),
+ ('deptId', models.CharField(default='', help_text='部门id', max_length=64, verbose_name='部门id')),
+ ('username', models.CharField(default='', help_text='用户账号', max_length=64, verbose_name='用户账号')),
+ ('nickName', models.CharField(default='', help_text='用户昵称', max_length=64, verbose_name='用户昵称')),
+ ('userType', models.CharField(default=False, help_text='用户类型(00系统用户)', max_length=4, verbose_name='用户类型(00系统用户)')),
+ ('email', models.CharField(default='', help_text='邮箱', max_length=64, verbose_name='邮箱')),
+ ('phone', models.CharField(default='', help_text='手机号码', max_length=64, verbose_name='手机号码')),
+ ('avatar', models.CharField(default='', help_text='头像', max_length=64, verbose_name='头像')),
+ ('password', models.CharField(default='', help_text='密码(加密后)', max_length=64, verbose_name='密码(加密后)')),
+ ('status', models.CharField(default='0', help_text='帐号状态(0正常 1停用)', max_length=4, verbose_name='帐号状态(0正常 1停用)')),
+ ('delFlag', models.CharField(default='0', help_text='删除标志(0代表存在 2代表删除)', max_length=4, verbose_name='删除标志(0代表存在 2代表删除)')),
+ ('loginIp', models.CharField(default='', help_text='最后登陆ip', max_length=64, verbose_name='最后登陆ip')),
+ ('loginDate', models.CharField(default='', help_text='最后登陆日期', max_length=64, verbose_name='最后登陆日期')),
+ ],
+ options={
+ 'verbose_name': '用户管理',
+ 'db_table': 'system_user',
+ },
+ ),
+ migrations.CreateModel(
+ name='SystemUserRole',
+ fields=[
+ ('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
+ ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
+ ('create_time', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
+ ('remark', models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息')),
+ ('createBy', models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者')),
+ ('userId', models.CharField(default='', help_text='用户id', max_length=64, verbose_name='菜单id')),
+ ('roleId', models.CharField(default='', help_text='角色id', max_length=64, verbose_name='角色id')),
+ ],
+ options={
+ 'verbose_name': '角色与人员关系映射',
+ 'db_table': 'system_role_user',
+ },
+ ),
+ migrations.AddField(
+ model_name='systemdept',
+ name='remark',
+ field=models.CharField(blank=True, help_text='备注信息', max_length=64, verbose_name='备注信息'),
+ ),
+ migrations.AlterField(
+ model_name='systemdept',
+ name='createBy',
+ field=models.CharField(blank=True, help_text='创建者', max_length=64, verbose_name='创建者'),
+ ),
+ migrations.AlterField(
+ model_name='systemdept',
+ name='delFlag',
+ field=models.CharField(default='0', help_text='是否删除 (0存在 2删除)', max_length=4, verbose_name='是否删除'),
+ ),
+ migrations.AlterField(
+ model_name='systemdept',
+ name='email',
+ field=models.CharField(default='', help_text='邮箱', max_length=32, verbose_name='邮箱'),
+ ),
+ migrations.AlterField(
+ model_name='systemdept',
+ name='leader',
+ field=models.CharField(default='', help_text='负责人', max_length=32, verbose_name='负责人'),
+ ),
+ migrations.AlterField(
+ model_name='systemdept',
+ name='phone',
+ field=models.CharField(default='', help_text='联系电话', max_length=32, verbose_name='联系电话'),
+ ),
+ migrations.AlterField(
+ model_name='systemdept',
+ name='status',
+ field=models.CharField(default='0', help_text='启用状态 (0正常 1停用)', max_length=4, verbose_name='启用状态'),
+ ),
+ ]
diff --git a/admin-api/web/migrations/0003_auto_20240531_2104.py b/admin-api/web/migrations/0003_auto_20240531_2104.py
new file mode 100644
index 0000000..5f7b09b
--- /dev/null
+++ b/admin-api/web/migrations/0003_auto_20240531_2104.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.2.25 on 2024-05-31 13:04
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('web', '0002_auto_20240531_1456'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='systeminit',
+ options={'verbose_name': '部门管理'},
+ ),
+ migrations.AlterModelTable(
+ name='systeminit',
+ table='system_init',
+ ),
+ ]
diff --git a/admin-api/web/migrations/0004_auto_20240614_1628.py b/admin-api/web/migrations/0004_auto_20240614_1628.py
new file mode 100644
index 0000000..27cdf7b
--- /dev/null
+++ b/admin-api/web/migrations/0004_auto_20240614_1628.py
@@ -0,0 +1,58 @@
+# Generated by Django 3.2.25 on 2024-06-14 08:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('web', '0003_auto_20240531_2104'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='systemmenu',
+ name='isCache',
+ field=models.CharField(default='0', help_text='是否缓存(0缓存 1不缓存)', max_length=4, verbose_name='是否缓存(0缓存 1不缓存)'),
+ ),
+ migrations.AlterField(
+ model_name='systemmenu',
+ name='isFrame',
+ field=models.CharField(default='1', help_text='是否为外链(0是 1否)', max_length=4, verbose_name='是否为外链(0是 1否)'),
+ ),
+ migrations.AlterField(
+ model_name='systemmenu',
+ name='status',
+ field=models.CharField(default='0', help_text='菜单状态(0正常 1停用)', max_length=4, verbose_name='菜单状态(0正常 1停用)'),
+ ),
+ migrations.AlterField(
+ model_name='systemmenu',
+ name='visible',
+ field=models.CharField(default='0', help_text='菜单状态(0显示 1隐藏)', max_length=4, verbose_name='菜单状态(0显示 1隐藏)'),
+ ),
+ migrations.AlterField(
+ model_name='systemuser',
+ name='userType',
+ field=models.CharField(default='00', help_text='用户类型(00系统用户)', max_length=4, verbose_name='用户类型(00系统用户)'),
+ ),
+ migrations.AddIndex(
+ model_name='systemdept',
+ index=models.Index(fields=['deptId'], name='SystemDept_deptId'),
+ ),
+ migrations.AddIndex(
+ model_name='systemmenu',
+ index=models.Index(fields=['menuId'], name='SystemMenu_menuId'),
+ ),
+ migrations.AddIndex(
+ model_name='systemrole',
+ index=models.Index(fields=['roleId'], name='SystemRole_roleId'),
+ ),
+ migrations.AddIndex(
+ model_name='systemrolemenu',
+ index=models.Index(fields=['roleId'], name='SystemRoleMenu_roleId'),
+ ),
+ migrations.AddIndex(
+ model_name='systemuser',
+ index=models.Index(fields=['userId'], name='SystemUser_userId'),
+ ),
+ ]
diff --git a/admin-api/web/migrations/__init__.py b/admin-api/web/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/admin-api/web/models.py b/admin-api/web/models.py
new file mode 100644
index 0000000..e6de983
--- /dev/null
+++ b/admin-api/web/models.py
@@ -0,0 +1,108 @@
+from django.db import models
+
+from database.core import CoreModel
+
+
+class SystemInit(CoreModel):
+ Init = models.BooleanField(default=False, blank=True, help_text="是否已经初始化数据库")
+
+ class Meta:
+ db_table = 'system_init'
+ verbose_name = '部门管理'
+
+
+class SystemDept(CoreModel):
+ parentId = models.CharField(max_length=64, default="", verbose_name="父id", help_text="父id")
+ deptName = models.CharField(max_length=64, default="", verbose_name="部门名称", help_text="部门名称")
+ deptId = models.CharField(unique=True, max_length=64, default="", verbose_name="部门id", help_text="部门id")
+ orderNum = models.IntegerField(default=0, verbose_name="排序", help_text="排序")
+ leader = models.CharField(max_length=32, default="", verbose_name="负责人", help_text="负责人")
+ phone = models.CharField(max_length=32, default="", verbose_name="联系电话", help_text="联系电话")
+ email = models.CharField(max_length=32, default="", verbose_name="邮箱", help_text="邮箱")
+ status = models.CharField(max_length=4, default="0", verbose_name="启用状态", help_text="启用状态 (0正常 1停用)")
+ delFlag = models.CharField(max_length=4, default="0", verbose_name="是否删除", help_text="是否删除 (0存在 2删除)")
+ ancestors = models.CharField(max_length=64, default="", verbose_name="上级目录id", help_text="上级目录id")
+
+ class Meta:
+ indexes = [models.Index(fields=['deptId'], name='SystemDept_deptId')]
+ db_table = 'system_dept'
+ verbose_name = '部门管理'
+
+
+class SystemMenu(CoreModel):
+ menuId = models.CharField(unique=True, max_length=64, default="", verbose_name="菜单ID", help_text="菜单ID")
+ menuName = models.CharField(max_length=64, default="", verbose_name="菜单名称", help_text="菜单名称")
+ parentId = models.CharField(max_length=64, default="", verbose_name="父菜单ID", help_text="父菜单ID")
+ orderNum = models.IntegerField(default=0, verbose_name="显示顺序", help_text="显示顺序")
+ path = models.CharField(max_length=64, default="", verbose_name="路由地址", help_text="路由地址")
+ component = models.CharField(max_length=64, default="", verbose_name="组件路径", help_text="组件路径")
+ query = models.CharField(max_length=64, default="", verbose_name="路由参数", help_text="路由参数")
+ isFrame = models.CharField(max_length=4, default="1", verbose_name="是否为外链(0是 1否)", help_text="是否为外链(0是 1否)")
+ isCache = models.CharField(max_length=4, default="0", verbose_name="是否缓存(0缓存 1不缓存)", help_text="是否缓存(0缓存 1不缓存)")
+ visible = models.CharField(max_length=4, default="0", verbose_name="菜单状态(0显示 1隐藏)", help_text="菜单状态(0显示 1隐藏)")
+ menuType = models.CharField(max_length=4, default="", verbose_name="菜单类型(M目录 C菜单 F按钮)", help_text="菜单类型(M目录 C菜单 F按钮)")
+ status = models.CharField(max_length=4, default="0", verbose_name="菜单状态(0正常 1停用)", help_text="菜单状态(0正常 1停用)")
+ perms = models.CharField(max_length=32, default="", verbose_name="权限标识", help_text="权限标识")
+ icon = models.CharField(max_length=32, default="", verbose_name="菜单图", help_text="菜单图")
+
+ class Meta:
+ db_table = 'system_menu'
+ verbose_name = '目录管理'
+ indexes = [models.Index(fields=['menuId'], name='SystemMenu_menuId')]
+
+
+class SystemRole(CoreModel):
+ roleId = models.CharField(unique=True, max_length=64, default="", verbose_name="角色id", help_text="角色id")
+ roleName = models.CharField(max_length=64, default="", verbose_name="角色名称", help_text="角色名称")
+ roleKey = models.CharField(max_length=64, default="", verbose_name="角色权限字符", help_text="角色权限字符")
+ roleSort = models.IntegerField(default=0, verbose_name="角色排序", help_text="角色排序")
+ roleAdmin = models.BooleanField(max_length=64, default=False, verbose_name="是否是超级管理员", help_text="是否是超级管理员")
+ status = models.CharField(max_length=4, default="0", verbose_name="启用状态 (0正常 1停用)", help_text="启用状态 (0正常 1停用)")
+ menuCheckStrictly = models.BooleanField(default=True, verbose_name="菜单树选择项是否关联显示", help_text="菜单树选择项是否关联显示")
+ deptCheckStrictly = models.BooleanField(default=True, verbose_name="部门树选择项是否关联显示", help_text="部门树选择项是否关联显示")
+ delFlag = models.CharField(max_length=4, default="0", verbose_name="是否删除 (0代表存在 2代表删除)", help_text="是否删除 (0代表存在 2代表删除)")
+
+ class Meta:
+ db_table = 'system_role'
+ verbose_name = '角色管理'
+ indexes = [models.Index(fields=['roleId'], name='SystemRole_roleId')]
+
+
+class SystemUser(CoreModel):
+ userId = models.CharField(unique=True, max_length=64, default="", verbose_name="用户id", help_text="用户id")
+ deptId = models.CharField(max_length=64, default="", verbose_name="部门id", help_text="部门id")
+ username = models.CharField(max_length=64, default="", verbose_name="用户账号", help_text="用户账号")
+ nickName = models.CharField(max_length=64, default="", verbose_name="用户昵称", help_text="用户昵称")
+ userType = models.CharField(max_length=4, default="00", verbose_name="用户类型(00系统用户)", help_text="用户类型(00系统用户)")
+ email = models.CharField(max_length=64, default="", verbose_name="邮箱", help_text="邮箱")
+ phone = models.CharField(max_length=64, default="", verbose_name="手机号码", help_text="手机号码")
+ avatar = models.CharField(max_length=64, default="", verbose_name="头像", help_text="头像")
+ password = models.CharField(max_length=64, default="", verbose_name="密码(加密后)", help_text="密码(加密后)")
+ status = models.CharField(max_length=4, default="0", verbose_name="帐号状态(0正常 1停用)", help_text="帐号状态(0正常 1停用)")
+ delFlag = models.CharField(max_length=4, default="0", verbose_name="删除标志(0代表存在 2代表删除)", help_text="删除标志(0代表存在 2代表删除)")
+ loginIp = models.CharField(max_length=64, default="", verbose_name="最后登陆ip", help_text="最后登陆ip")
+ loginDate = models.CharField(max_length=64, default="", verbose_name="最后登陆日期", help_text="最后登陆日期")
+
+ class Meta:
+ db_table = 'system_user'
+ verbose_name = '用户管理'
+ indexes = [models.Index(fields=['userId'], name='SystemUser_userId')]
+
+
+class SystemRoleMenu(CoreModel):
+ menuId = models.CharField(max_length=64, default="", verbose_name="菜单id", help_text="菜单id")
+ roleId = models.CharField(max_length=64, default="", verbose_name="角色id", help_text="角色id")
+
+ class Meta:
+ db_table = 'system_role_menu'
+ verbose_name = '角色与目录关系映射'
+ indexes = [models.Index(fields=['roleId'], name='SystemRoleMenu_roleId')]
+
+
+class SystemUserRole(CoreModel):
+ userId = models.CharField(max_length=64, default="", verbose_name="菜单id", help_text="用户id")
+ roleId = models.CharField(max_length=64, default="", verbose_name="角色id", help_text="角色id")
+
+ class Meta:
+ db_table = 'system_role_user'
+ verbose_name = '角色与人员关系映射'
diff --git a/admin-api/web/paginator.py b/admin-api/web/paginator.py
new file mode 100644
index 0000000..e2f6c3a
--- /dev/null
+++ b/admin-api/web/paginator.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+@Author :mengying
+@Date :2024/6/13 14:27
+@Email : 652044581@qq.com
+@Desc : 分页器
+"""
+from rest_framework.exceptions import NotFound
+from rest_framework.pagination import PageNumberPagination
+
+
+class StandardResultsSetPagination(PageNumberPagination):
+ # 默认每页显示的数据条数
+ page_size = 10
+
+ # 获取URL参数中设置的每页显示数据条数
+ page_size_query_param = 'pageSize'
+
+ # 获取URL参数中传入的页码key
+ page_query_param = 'pageNum'
+
+ # 最大支持的每页显示的数据条数
+ max_page_size = 50
+
+ def paginate_queryset_data(self, queryset, request, view=None, serializer=None):
+ try:
+ queryset = super().paginate_queryset(queryset, request=request, view=view)
+ ser = serializer(queryset, many=True)
+ return ser.data
+ except NotFound as e:
+ return []
+
+ def paginate_queryset_count(self, queryset, request, view=None, serializer=None):
+ try:
+ res = {}
+ queryset = super().paginate_queryset(queryset, request=request, view=view)
+ ser = serializer(queryset, many=True)
+ res["data"] = ser.data
+ res["count"] = self.page.paginator.count
+ return res
+ except NotFound as e:
+ return {}
diff --git a/admin-api/web/serializer.py b/admin-api/web/serializer.py
new file mode 100644
index 0000000..41c058b
--- /dev/null
+++ b/admin-api/web/serializer.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+"""
+@Author :mengying
+@Date :2024/6/3 17:19
+@Email : 652044581@qq.com
+@Desc : 功能描述
+"""
+from rest_framework import serializers
+
+from web.models import SystemUser, SystemUserRole, SystemMenu, SystemRole, SystemDept
+
+
+class SystemUserSerializer(serializers.ModelSerializer):
+ update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+ create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+
+ class Meta:
+ model = SystemUser
+ fields = '__all__'
+
+
+class SystemDeptSerializer(serializers.ModelSerializer):
+ update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+ create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+
+ class Meta:
+ model = SystemDept
+ fields = '__all__'
+
+
+class SystemRoleSerializer(serializers.ModelSerializer):
+ update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+ create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+
+ class Meta:
+ model = SystemRole
+ fields = '__all__'
+
+
+class SystemMenuSerializer(serializers.ModelSerializer):
+ update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+ create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+
+ class Meta:
+ model = SystemMenu
+ fields = '__all__'
+
+
+class SystemUserRoleSerializer(serializers.ModelSerializer):
+ update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+ create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
+
+ class Meta:
+ model = SystemUserRole
+ fields = '__all__'
diff --git a/admin-api/web/static/fontBold.ttf b/admin-api/web/static/fontBold.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..02bb4a6843e9de3475094ad90915091eb0c7ebb7
GIT binary patch
literal 705684
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