├── .github
├── FUNDING.yml
└── workflows
│ └── test.yml
├── ruoyi-fastapi-backend
├── module_task
│ ├── __init__.py
│ └── scheduler_test.py
├── alembic
│ ├── README
│ └── script.py.mako
├── assets
│ └── font
│ │ └── Arial.ttf
├── utils
│ ├── message_util.py
│ ├── pwd_util.py
│ ├── dependency_util.py
│ └── log_util.py
├── common
│ ├── aspect
│ │ └── db_seesion.py
│ └── enums.py
├── sub_applications
│ ├── handle.py
│ └── staticfiles.py
├── Dockerfile.my
├── Dockerfile.pg
├── middlewares
│ ├── gzip_middleware.py
│ ├── trace_middleware
│ │ ├── __init__.py
│ │ ├── ctx.py
│ │ ├── middle.py
│ │ └── span.py
│ ├── cors_middleware.py
│ ├── handle.py
│ └── context_middleware.py
├── app.py
├── requirements.txt
├── requirements-pg.txt
├── module_admin
│ ├── entity
│ │ ├── vo
│ │ │ ├── common_vo.py
│ │ │ ├── cache_vo.py
│ │ │ └── online_vo.py
│ │ └── do
│ │ │ ├── post_do.py
│ │ │ ├── config_do.py
│ │ │ ├── notice_do.py
│ │ │ └── dept_do.py
│ ├── dao
│ │ └── login_dao.py
│ ├── controller
│ │ ├── server_controller.py
│ │ └── captcha_controller.py
│ └── service
│ │ └── captcha_service.py
├── config
│ ├── get_db.py
│ └── database.py
├── module_generator
│ └── templates
│ │ ├── js
│ │ └── api.js.jinja2
│ │ └── python
│ │ └── do.py.jinja2
├── .env.dev
├── .env.prod
├── .env.dockermy
├── .env.dockerpg
└── exceptions
│ └── exception.py
├── ruoyi-fastapi-frontend
├── src
│ ├── store
│ │ ├── index.js
│ │ └── modules
│ │ │ ├── app.js
│ │ │ ├── dict.js
│ │ │ └── settings.js
│ ├── components
│ │ ├── ParentView
│ │ │ └── index.vue
│ │ ├── IconSelect
│ │ │ └── requireIcons.js
│ │ ├── RuoYi
│ │ │ ├── Doc
│ │ │ │ └── index.vue
│ │ │ └── Git
│ │ │ │ └── index.vue
│ │ ├── SvgIcon
│ │ │ ├── svgicon.js
│ │ │ └── index.vue
│ │ ├── Screenfull
│ │ │ └── index.vue
│ │ ├── iFrame
│ │ │ └── index.vue
│ │ ├── Hamburger
│ │ │ └── index.vue
│ │ └── SizeSelect
│ │ │ └── index.vue
│ ├── assets
│ │ ├── logo
│ │ │ └── logo.png
│ │ ├── 401_images
│ │ │ └── 401.gif
│ │ ├── 404_images
│ │ │ ├── 404.png
│ │ │ └── 404_cloud.png
│ │ ├── images
│ │ │ ├── profile.jpg
│ │ │ └── login-background.jpg
│ │ ├── icons
│ │ │ └── svg
│ │ │ │ ├── chart.svg
│ │ │ │ ├── size.svg
│ │ │ │ ├── link.svg
│ │ │ │ ├── guide.svg
│ │ │ │ ├── money.svg
│ │ │ │ ├── email.svg
│ │ │ │ ├── drag.svg
│ │ │ │ ├── documentation.svg
│ │ │ │ ├── fullscreen.svg
│ │ │ │ ├── user.svg
│ │ │ │ ├── lock.svg
│ │ │ │ ├── excel.svg
│ │ │ │ ├── example.svg
│ │ │ │ ├── slider.svg
│ │ │ │ ├── star.svg
│ │ │ │ ├── table.svg
│ │ │ │ ├── search.svg
│ │ │ │ ├── moon.svg
│ │ │ │ ├── education.svg
│ │ │ │ ├── tab.svg
│ │ │ │ ├── message.svg
│ │ │ │ ├── switch.svg
│ │ │ │ ├── theme.svg
│ │ │ │ ├── code.svg
│ │ │ │ ├── druid.svg
│ │ │ │ ├── peoples.svg
│ │ │ │ ├── input.svg
│ │ │ │ ├── server.svg
│ │ │ │ ├── textarea.svg
│ │ │ │ ├── time.svg
│ │ │ │ ├── edit.svg
│ │ │ │ ├── nested.svg
│ │ │ │ ├── row.svg
│ │ │ │ ├── monitor.svg
│ │ │ │ ├── tree-table.svg
│ │ │ │ ├── eye.svg
│ │ │ │ ├── build.svg
│ │ │ │ ├── clipboard.svg
│ │ │ │ ├── list.svg
│ │ │ │ ├── download.svg
│ │ │ │ ├── icon.svg
│ │ │ │ ├── enter.svg
│ │ │ │ ├── international.svg
│ │ │ │ ├── more-up.svg
│ │ │ │ ├── question.svg
│ │ │ │ ├── wechat.svg
│ │ │ │ ├── skill.svg
│ │ │ │ ├── people.svg
│ │ │ │ ├── post.svg
│ │ │ │ ├── checkbox.svg
│ │ │ │ ├── language.svg
│ │ │ │ ├── eye-open.svg
│ │ │ │ ├── validCode.svg
│ │ │ │ ├── radio.svg
│ │ │ │ ├── select.svg
│ │ │ │ ├── upload.svg
│ │ │ │ ├── sunny.svg
│ │ │ │ ├── 404.svg
│ │ │ │ ├── zip.svg
│ │ │ │ ├── phone.svg
│ │ │ │ ├── log.svg
│ │ │ │ ├── bug.svg
│ │ │ │ ├── github.svg
│ │ │ │ ├── pdf.svg
│ │ │ │ ├── logininfor.svg
│ │ │ │ ├── rate.svg
│ │ │ │ ├── job.svg
│ │ │ │ ├── exit-fullscreen.svg
│ │ │ │ ├── tree.svg
│ │ │ │ ├── swagger.svg
│ │ │ │ ├── password.svg
│ │ │ │ └── date-range.svg
│ │ └── styles
│ │ │ ├── transition.scss
│ │ │ ├── mixin.scss
│ │ │ ├── element-ui.scss
│ │ │ └── btn.scss
│ ├── utils
│ │ ├── errorCode.js
│ │ ├── auth.js
│ │ ├── dynamicTitle.js
│ │ ├── generator
│ │ │ ├── css.js
│ │ │ └── drawingDefault.js
│ │ ├── dict.js
│ │ ├── jsencrypt.js
│ │ ├── permission.js
│ │ ├── theme.js
│ │ └── scroll-to.js
│ ├── api
│ │ ├── menu.js
│ │ ├── monitor
│ │ │ ├── server.js
│ │ │ ├── online.js
│ │ │ ├── operlog.js
│ │ │ ├── jobLog.js
│ │ │ ├── logininfor.js
│ │ │ ├── cache.js
│ │ │ └── job.js
│ │ ├── system
│ │ │ ├── post.js
│ │ │ ├── notice.js
│ │ │ ├── dept.js
│ │ │ ├── dict
│ │ │ │ ├── data.js
│ │ │ │ └── type.js
│ │ │ ├── menu.js
│ │ │ └── config.js
│ │ ├── login.js
│ │ └── tool
│ │ │ └── gen.js
│ ├── layout
│ │ └── components
│ │ │ ├── index.js
│ │ │ ├── Copyright
│ │ │ └── index.vue
│ │ │ ├── IframeToggle
│ │ │ └── index.vue
│ │ │ ├── Sidebar
│ │ │ └── Link.vue
│ │ │ └── InnerLink
│ │ │ └── index.vue
│ ├── views
│ │ ├── tool
│ │ │ ├── swagger
│ │ │ │ └── index.vue
│ │ │ ├── gen
│ │ │ │ ├── createTable.vue
│ │ │ │ └── basicInfoForm.vue
│ │ │ └── build
│ │ │ │ └── CodeTypeDialog.vue
│ │ ├── redirect
│ │ │ └── index.vue
│ │ ├── monitor
│ │ │ └── druid
│ │ │ │ └── index.vue
│ │ ├── dashboard
│ │ │ └── editable-link-group.vue
│ │ └── error
│ │ │ └── 401.vue
│ ├── directive
│ │ ├── index.js
│ │ ├── permission
│ │ │ ├── hasRole.js
│ │ │ └── hasPermi.js
│ │ └── common
│ │ │ └── copyText.js
│ ├── App.vue
│ ├── plugins
│ │ ├── index.js
│ │ ├── auth.js
│ │ ├── cache.js
│ │ └── modal.js
│ └── settings.js
├── bin
│ ├── build.bat
│ ├── package.bat
│ ├── run-web.bat
│ ├── nginx.dockermy.conf
│ └── nginx.dockerpg.conf
├── public
│ └── favicon.ico
├── .env.development
├── vite
│ └── plugins
│ │ ├── setup-extend.js
│ │ ├── auto-import.js
│ │ ├── svg-icon.js
│ │ ├── index.js
│ │ └── compression.js
├── .env.docker
├── .env.staging
├── .env.production
├── .gitignore
├── Dockerfile
├── LICENSE
└── package.json
├── LICENSE
├── docker-compose.pg.yml
└── docker-compose.my.yml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [insistence]
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_task/__init__.py:
--------------------------------------------------------------------------------
1 | from . import scheduler_test # noqa: F401
2 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/alembic/README:
--------------------------------------------------------------------------------
1 | Generic single-database configuration with an async dbapi.
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/store/index.js:
--------------------------------------------------------------------------------
1 | const store = createPinia()
2 |
3 | export default store
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/ParentView/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/bin/build.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/bin/build.bat
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/bin/package.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/bin/package.bat
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/bin/run-web.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/bin/run-web.bat
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/public/favicon.ico
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/assets/font/Arial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-backend/assets/font/Arial.ttf
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/src/assets/logo/logo.png
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/401_images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/src/assets/401_images/401.gif
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/404_images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/src/assets/404_images/404.png
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/images/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/src/assets/images/profile.jpg
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/utils/message_util.py:
--------------------------------------------------------------------------------
1 | from utils.log_util import logger
2 |
3 |
4 | def message_service(sms_code: str) -> None:
5 | logger.info(f'短信验证码为{sms_code}')
6 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/404_images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/src/assets/404_images/404_cloud.png
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/images/login-background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insistence/RuoYi-Vue3-FastAPI/HEAD/ruoyi-fastapi-frontend/src/assets/images/login-background.jpg
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/errorCode.js:
--------------------------------------------------------------------------------
1 | export default {
2 | '401': '认证失败,无法访问系统资源',
3 | '403': '当前操作没有权限',
4 | '404': '访问资源不存在',
5 | 'default': '系统未知错误,请反馈给管理员'
6 | }
7 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/.env.development:
--------------------------------------------------------------------------------
1 | # 页面标题
2 | VITE_APP_TITLE = vfadmin管理系统
3 |
4 | # 开发环境配置
5 | VITE_APP_ENV = 'development'
6 |
7 | # vfadmin管理系统/开发环境
8 | VITE_APP_BASE_API = '/dev-api'
9 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/vite/plugins/setup-extend.js:
--------------------------------------------------------------------------------
1 | import setupExtend from 'unplugin-vue-setup-extend-plus/vite'
2 |
3 | export default function createSetupExtend() {
4 | return setupExtend({})
5 | }
6 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/menu.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 获取路由
4 | export const getRouters = () => {
5 | return request({
6 | url: '/getRouters',
7 | method: 'get'
8 | })
9 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/server.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 获取服务信息
4 | export function getServer() {
5 | return request({
6 | url: '/monitor/server',
7 | method: 'get'
8 | })
9 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/chart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/size.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/.env.docker:
--------------------------------------------------------------------------------
1 | # 页面标题
2 | VITE_APP_TITLE = vfadmin管理系统
3 |
4 | # 生产环境配置
5 | VITE_APP_ENV = 'docker'
6 |
7 | # vfadmin管理系统/生产环境
8 | VITE_APP_BASE_API = '/docker-api'
9 |
10 | # 是否在打包时开启压缩,支持 gzip 和 brotli
11 | VITE_BUILD_COMPRESS = gzip
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/.env.staging:
--------------------------------------------------------------------------------
1 | # 页面标题
2 | VITE_APP_TITLE = vfadmin管理系统
3 |
4 | # 生产环境配置
5 | VITE_APP_ENV = 'staging'
6 |
7 | # vfadmin管理系统/生产环境
8 | VITE_APP_BASE_API = '/stage-api'
9 |
10 | # 是否在打包时开启压缩,支持 gzip 和 brotli
11 | VITE_BUILD_COMPRESS = gzip
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/.env.production:
--------------------------------------------------------------------------------
1 | # 页面标题
2 | VITE_APP_TITLE = vfadmin管理系统
3 |
4 | # 生产环境配置
5 | VITE_APP_ENV = 'production'
6 |
7 | # vfadmin管理系统/生产环境
8 | VITE_APP_BASE_API = '/prod-api'
9 |
10 | # 是否在打包时开启压缩,支持 gzip 和 brotli
11 | VITE_BUILD_COMPRESS = gzip
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/layout/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as AppMain } from './AppMain'
2 | export { default as Navbar } from './Navbar'
3 | export { default as Settings } from './Settings'
4 | export { default as TagsView } from './TagsView/index.vue'
5 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/tool/swagger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/IconSelect/requireIcons.js:
--------------------------------------------------------------------------------
1 | let icons = []
2 | const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
3 | for (const path in modules) {
4 | const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
5 | icons.push(p);
6 | }
7 |
8 | export default icons
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/common/aspect/db_seesion.py:
--------------------------------------------------------------------------------
1 | from fastapi import Depends, params
2 |
3 | from config.get_db import get_db
4 |
5 |
6 | def DBSessionDependency() -> params.Depends: # noqa: N802
7 | """
8 | 数据库会话依赖
9 |
10 | :return: 数据库会话依赖
11 | """
12 | return Depends(get_db)
13 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/sub_applications/handle.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 |
3 | from sub_applications.staticfiles import mount_staticfiles
4 |
5 |
6 | def handle_sub_applications(app: FastAPI) -> None:
7 | """
8 | 全局处理子应用挂载
9 | """
10 | # 挂载静态文件
11 | mount_staticfiles(app)
12 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/Dockerfile.my:
--------------------------------------------------------------------------------
1 | FROM python:3.10
2 | WORKDIR /app
3 |
4 | # 复制源代码
5 | COPY . .
6 |
7 | # 安装依赖
8 | RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
9 |
10 | # 暴露端口
11 | EXPOSE 9099
12 |
13 | # 启动命令
14 | CMD ["python", "app.py", "--env=dockermy"]
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/Dockerfile.pg:
--------------------------------------------------------------------------------
1 | FROM python:3.10
2 | WORKDIR /app
3 |
4 | # 复制源代码
5 | COPY . .
6 |
7 | # 安装依赖
8 | RUN pip install --no-cache-dir -r requirements-pg.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
9 |
10 | # 暴露端口
11 | EXPOSE 9099
12 |
13 | # 启动命令
14 | CMD ["python", "app.py", "--env=dockerpg"]
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/RuoYi/Doc/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/RuoYi/Git/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/vite/plugins/auto-import.js:
--------------------------------------------------------------------------------
1 | import autoImport from 'unplugin-auto-import/vite'
2 |
3 | export default function createAutoImport() {
4 | return autoImport({
5 | imports: [
6 | 'vue',
7 | 'vue-router',
8 | 'pinia'
9 | ],
10 | dts: false
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/directive/index.js:
--------------------------------------------------------------------------------
1 | import hasRole from './permission/hasRole'
2 | import hasPermi from './permission/hasPermi'
3 | import copyText from './common/copyText'
4 |
5 | export default function directive(app){
6 | app.directive('hasRole', hasRole)
7 | app.directive('hasPermi', hasPermi)
8 | app.directive('copyText', copyText)
9 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/guide.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/SvgIcon/svgicon.js:
--------------------------------------------------------------------------------
1 | import * as components from '@element-plus/icons-vue'
2 |
3 | export default {
4 | install: (app) => {
5 | for (const key in components) {
6 | const componentConfig = components[key];
7 | app.component(componentConfig.name, componentConfig);
8 | }
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/money.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const TokenKey = 'Admin-Token'
4 |
5 | export function getToken() {
6 | return Cookies.get(TokenKey)
7 | }
8 |
9 | export function setToken(token) {
10 | return Cookies.set(TokenKey, token)
11 | }
12 |
13 | export function removeToken() {
14 | return Cookies.remove(TokenKey)
15 | }
16 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/redirect/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/gzip_middleware.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 | from starlette.middleware.gzip import GZipMiddleware
3 |
4 |
5 | def add_gzip_middleware(app: FastAPI) -> None:
6 | """
7 | 添加gzip压缩中间件
8 |
9 | :param app: FastAPI对象
10 | :return:
11 | """
12 | app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=9)
13 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | **/*.log
8 |
9 | tests/**/coverage/
10 | tests/e2e/reports
11 | selenium-debug.log
12 |
13 | # Editor directories and files
14 | .idea
15 | .vscode
16 | *.suo
17 | *.ntvs*
18 | *.njsproj
19 | *.sln
20 | *.local
21 |
22 | package-lock.json
23 | yarn.lock
24 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/drag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/sub_applications/staticfiles.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 | from fastapi.staticfiles import StaticFiles
3 |
4 | from config.env import UploadConfig
5 |
6 |
7 | def mount_staticfiles(app: FastAPI) -> None:
8 | """
9 | 挂载静态文件
10 | """
11 | app.mount(f'{UploadConfig.UPLOAD_PREFIX}', StaticFiles(directory=f'{UploadConfig.UPLOAD_PATH}'), name='profile')
12 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/vite/plugins/svg-icon.js:
--------------------------------------------------------------------------------
1 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
2 | import path from 'path'
3 |
4 | export default function createSvgIcon(isBuild) {
5 | return createSvgIconsPlugin({
6 | iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
7 | symbolId: 'icon-[dir]-[name]',
8 | svgoOptions: isBuild
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/monitor/druid/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
15 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/app.py:
--------------------------------------------------------------------------------
1 | import uvicorn
2 |
3 | from config.env import AppConfig
4 | from server import create_app
5 |
6 | app = create_app()
7 |
8 | if __name__ == '__main__':
9 | uvicorn.run(
10 | app='app:app',
11 | host=AppConfig.app_host,
12 | port=AppConfig.app_port,
13 | root_path=AppConfig.app_root_path,
14 | reload=AppConfig.app_reload,
15 | )
16 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/online.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询在线用户列表
4 | export function list(query) {
5 | return request({
6 | url: '/monitor/online/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 强退用户
13 | export function forceLogout(tokenId) {
14 | return request({
15 | url: '/monitor/online/' + tokenId,
16 | method: 'delete'
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_task/scheduler_test.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 |
4 | def job(*args, **kwargs) -> None:
5 | """
6 | 定时任务执行同步函数示例
7 | """
8 | print(args)
9 | print(kwargs)
10 | print(f'{datetime.now()}同步函数执行了')
11 |
12 |
13 | async def async_job(*args, **kwargs) -> None:
14 | """
15 | 定时任务执行异步函数示例
16 | """
17 | print(args)
18 | print(kwargs)
19 | print(f'{datetime.now()}异步函数执行了')
20 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/trace_middleware/__init__.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 |
3 | from .ctx import TraceCtx
4 | from .middle import TraceASGIMiddleware
5 |
6 | __all__ = ('TraceASGIMiddleware', 'TraceCtx')
7 |
8 | __version__ = '0.1.0'
9 |
10 |
11 | def add_trace_middleware(app: FastAPI) -> None:
12 | """
13 | 添加trace中间件
14 |
15 | :param app: FastAPI对象
16 | :return:
17 | """
18 | app.add_middleware(TraceASGIMiddleware)
19 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/documentation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/dynamicTitle.js:
--------------------------------------------------------------------------------
1 | import defaultSettings from '@/settings'
2 | import useSettingsStore from '@/store/modules/settings'
3 |
4 | /**
5 | * 动态修改标题
6 | */
7 | export function useDynamicTitle() {
8 | const settingsStore = useSettingsStore();
9 | if (settingsStore.dynamicTitle) {
10 | document.title = settingsStore.title + ' - ' + defaultSettings.title;
11 | } else {
12 | document.title = defaultSettings.title;
13 | }
14 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/trace_middleware/ctx.py:
--------------------------------------------------------------------------------
1 | import contextvars
2 | from uuid import uuid4
3 |
4 | CTX_REQUEST_ID: contextvars.ContextVar[str] = contextvars.ContextVar('request-id', default='')
5 |
6 |
7 | class TraceCtx:
8 | @staticmethod
9 | def set_id() -> str:
10 | _id = uuid4().hex
11 | CTX_REQUEST_ID.set(_id)
12 | return _id
13 |
14 | @staticmethod
15 | def get_id() -> str:
16 | return CTX_REQUEST_ID.get()
17 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/requirements.txt:
--------------------------------------------------------------------------------
1 | aiofiles==25.1.0
2 | alembic==1.16.5
3 | APScheduler==3.11.1
4 | async-lru==2.0.5
5 | asyncmy==0.2.10
6 | bcrypt==5.0.0
7 | fastapi[all]==0.125.0
8 | loguru==0.7.3
9 | openpyxl==3.1.5
10 | pandas==2.3.3
11 | Pillow==11.3.0
12 | psutil==7.1.3
13 | pydantic-validation-decorator==0.1.5
14 | PyJWT[crypto]==2.10.1
15 | PyMySQL==1.1.2
16 | redis==6.4.0
17 | ruff==0.14.10
18 | SQLAlchemy[asyncio]==2.0.45
19 | sqlglot[rs]==28.5.0
20 | user-agents==2.2.0
21 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/requirements-pg.txt:
--------------------------------------------------------------------------------
1 | aiofiles==25.1.0
2 | alembic==1.16.5
3 | APScheduler==3.11.1
4 | async-lru==2.0.5
5 | asyncpg==0.31.0
6 | bcrypt==5.0.0
7 | fastapi[all]==0.125.0
8 | loguru==0.7.3
9 | openpyxl==3.1.5
10 | pandas==2.3.3
11 | Pillow==11.3.0
12 | psutil==7.1.3
13 | pydantic-validation-decorator==0.1.5
14 | PyJWT[crypto]==2.10.1
15 | psycopg2==2.9.11
16 | redis==6.4.0
17 | ruff==0.14.10
18 | SQLAlchemy[asyncio]==2.0.45
19 | sqlglot[rs]==28.5.0
20 | user-agents==2.2.0
21 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/excel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | # 构建阶段
2 | FROM node:18-slim AS builder
3 | WORKDIR /app
4 |
5 | # 复制源代码
6 | COPY . .
7 |
8 | # 设置npm镜像源
9 | RUN npm config set registry https://registry.npmmirror.com
10 |
11 | # 安装依赖
12 | RUN npm install
13 |
14 | # 执行docker构建命令
15 | RUN npm run build:docker
16 |
17 | # 运行阶段
18 | FROM nginx:latest
19 | WORKDIR /usr/share/nginx/html
20 |
21 | # 复制构建产物
22 | COPY --from=builder /app/dist .
23 |
24 | # 暴露端口
25 | EXPOSE 80
26 |
27 | # 启动nginx
28 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | import tab from './tab'
2 | import auth from './auth'
3 | import cache from './cache'
4 | import modal from './modal'
5 | import download from './download'
6 |
7 | export default function installPlugins(app){
8 | // 页签操作
9 | app.config.globalProperties.$tab = tab
10 | // 认证对象
11 | app.config.globalProperties.$auth = auth
12 | // 缓存对象
13 | app.config.globalProperties.$cache = cache
14 | // 模态框对象
15 | app.config.globalProperties.$modal = modal
16 | // 下载文件
17 | app.config.globalProperties.$download = download
18 | }
19 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/Screenfull/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/operlog.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询操作日志列表
4 | export function list(query) {
5 | return request({
6 | url: '/monitor/operlog/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 删除操作日志
13 | export function delOperlog(operId) {
14 | return request({
15 | url: '/monitor/operlog/' + operId,
16 | method: 'delete'
17 | })
18 | }
19 |
20 | // 清空操作日志
21 | export function cleanOperlog() {
22 | return request({
23 | url: '/monitor/operlog/clean',
24 | method: 'delete'
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/slider.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/jobLog.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询调度日志列表
4 | export function listJobLog(query) {
5 | return request({
6 | url: '/monitor/jobLog/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 删除调度日志
13 | export function delJobLog(jobLogId) {
14 | return request({
15 | url: '/monitor/jobLog/' + jobLogId,
16 | method: 'delete'
17 | })
18 | }
19 |
20 | // 清空调度日志
21 | export function cleanJobLog() {
22 | return request({
23 | url: '/monitor/jobLog/clean',
24 | method: 'delete'
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/generator/css.js:
--------------------------------------------------------------------------------
1 | const styles = {
2 | 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
3 | 'el-upload': '.el-upload__tip{line-height: 1.2;}'
4 | }
5 |
6 | function addCss(cssList, el) {
7 | const css = styles[el.tag]
8 | css && cssList.indexOf(css) === -1 && cssList.push(css)
9 | if (el.children) {
10 | el.children.forEach(el2 => addCss(cssList, el2))
11 | }
12 | }
13 |
14 | export function makeUpCss(conf) {
15 | const cssList = []
16 | conf.fields.forEach(el => addCss(cssList, el))
17 | return cssList.join('\n')
18 | }
19 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/vite/plugins/index.js:
--------------------------------------------------------------------------------
1 | import vue from '@vitejs/plugin-vue'
2 |
3 | import createAutoImport from './auto-import'
4 | import createSvgIcon from './svg-icon'
5 | import createCompression from './compression'
6 | import createSetupExtend from './setup-extend'
7 |
8 | export default function createVitePlugins(viteEnv, isBuild = false) {
9 | const vitePlugins = [vue()]
10 | vitePlugins.push(createAutoImport())
11 | vitePlugins.push(createSetupExtend())
12 | vitePlugins.push(createSvgIcon(isBuild))
13 | isBuild && vitePlugins.push(...createCompression(viteEnv))
14 | return vitePlugins
15 | }
16 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/cors_middleware.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 | from fastapi.middleware.cors import CORSMiddleware
3 |
4 |
5 | def add_cors_middleware(app: FastAPI) -> None:
6 | """
7 | 添加跨域中间件
8 |
9 | :param app: FastAPI对象
10 | :return:
11 | """
12 | # 前端页面url
13 | origins = [
14 | 'http://localhost:80',
15 | 'http://127.0.0.1:80',
16 | ]
17 |
18 | # 后台api允许跨域
19 | app.add_middleware(
20 | CORSMiddleware,
21 | allow_origins=origins,
22 | allow_credentials=True,
23 | allow_methods=['*'],
24 | allow_headers=['*'],
25 | )
26 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/moon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/education.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/vo/common_vo.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel, ConfigDict, Field
4 | from pydantic.alias_generators import to_camel
5 |
6 |
7 | class UploadResponseModel(BaseModel):
8 | """
9 | 上传响应模型
10 | """
11 |
12 | model_config = ConfigDict(alias_generator=to_camel)
13 |
14 | file_name: Optional[str] = Field(default=None, description='新文件映射路径')
15 | new_file_name: Optional[str] = Field(default=None, description='新文件名称')
16 | original_filename: Optional[str] = Field(default=None, description='原文件名称')
17 | url: Optional[str] = Field(default=None, description='新文件url')
18 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/handle.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI
2 |
3 | from middlewares.context_middleware import add_context_cleanup_middleware
4 | from middlewares.cors_middleware import add_cors_middleware
5 | from middlewares.gzip_middleware import add_gzip_middleware
6 | from middlewares.trace_middleware import add_trace_middleware
7 |
8 |
9 | def handle_middleware(app: FastAPI) -> None:
10 | """
11 | 全局中间件处理
12 | """
13 | # 加载上下文清理中间件
14 | add_context_cleanup_middleware(app)
15 | # 加载跨域中间件
16 | add_cors_middleware(app)
17 | # 加载gzip压缩中间件
18 | add_gzip_middleware(app)
19 | # 加载trace中间件
20 | add_trace_middleware(app)
21 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/message.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/switch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/theme.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | lint-format:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v5
14 |
15 | - name: Set up Python
16 | uses: actions/setup-python@v5
17 | with:
18 | python-version: "3.9"
19 |
20 | - name: Install dependencies
21 | run: |
22 | python -m pip install --upgrade pip
23 | pip install ruff
24 |
25 | - name: Run linting
26 | run: |
27 | ruff check ruoyi-fastapi-backend
28 |
29 | - name: Run format check
30 | run: |
31 | ruff format ruoyi-fastapi-backend --check
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/code.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/druid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/bin/nginx.dockermy.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 |
5 | location / {
6 | root /usr/share/nginx/html;
7 | index index.html index.htm;
8 | try_files $uri $uri/ /index.html;
9 | }
10 |
11 | # API代理配置(MySQL版本)
12 | location /docker-api/ {
13 | proxy_pass http://ruoyi-backend-my:9099/;
14 | proxy_set_header Host $host;
15 | proxy_set_header X-Real-IP $remote_addr;
16 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
17 | proxy_set_header X-Forwarded-Proto $scheme;
18 | }
19 |
20 | error_page 500 502 503 504 /50x.html;
21 | location = /50x.html {
22 | root /usr/share/nginx/html;
23 | }
24 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/bin/nginx.dockerpg.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 |
5 | location / {
6 | root /usr/share/nginx/html;
7 | index index.html index.htm;
8 | try_files $uri $uri/ /index.html;
9 | }
10 |
11 | # API代理配置(PostgreSQL版本)
12 | location /docker-api/ {
13 | proxy_pass http://ruoyi-backend-pg:9099/;
14 | proxy_set_header Host $host;
15 | proxy_set_header X-Real-IP $remote_addr;
16 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
17 | proxy_set_header X-Forwarded-Proto $scheme;
18 | }
19 |
20 | error_page 500 502 503 504 /50x.html;
21 | location = /50x.html {
22 | root /usr/share/nginx/html;
23 | }
24 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/peoples.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/input.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/server.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/directive/permission/hasRole.js:
--------------------------------------------------------------------------------
1 | /**
2 | * v-hasRole 角色权限处理
3 | * Copyright (c) 2019 ruoyi
4 | */
5 | import useUserStore from '@/store/modules/user'
6 |
7 | export default {
8 | mounted(el, binding, vnode) {
9 | const { value } = binding
10 | const super_admin = "admin";
11 | const roles = useUserStore().roles
12 |
13 | if (value && value instanceof Array && value.length > 0) {
14 | const roleFlag = value
15 |
16 | const hasRole = roles.some(role => {
17 | return super_admin === role || roleFlag.includes(role)
18 | })
19 |
20 | if (!hasRole) {
21 | el.parentNode && el.parentNode.removeChild(el)
22 | }
23 | } else {
24 | throw new Error(`请设置角色权限标签值`)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/config/get_db.py:
--------------------------------------------------------------------------------
1 | from collections.abc import AsyncGenerator
2 |
3 | from sqlalchemy.ext.asyncio import AsyncSession
4 |
5 | from config.database import AsyncSessionLocal, Base, async_engine
6 | from utils.log_util import logger
7 |
8 |
9 | async def get_db() -> AsyncGenerator[AsyncSession, None]:
10 | """
11 | 每一个请求处理完毕后会关闭当前连接,不同的请求使用不同的连接
12 |
13 | :return:
14 | """
15 | async with AsyncSessionLocal() as current_db:
16 | yield current_db
17 |
18 |
19 | async def init_create_table() -> None:
20 | """
21 | 应用启动时初始化数据库连接
22 |
23 | :return:
24 | """
25 | logger.info('🔎 初始化数据库连接...')
26 | async with async_engine.begin() as conn:
27 | await conn.run_sync(Base.metadata.create_all)
28 | logger.info('✅️ 数据库连接成功')
29 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/logininfor.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询登录日志列表
4 | export function list(query) {
5 | return request({
6 | url: '/monitor/logininfor/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 删除登录日志
13 | export function delLogininfor(infoId) {
14 | return request({
15 | url: '/monitor/logininfor/' + infoId,
16 | method: 'delete'
17 | })
18 | }
19 |
20 | // 解锁用户登录状态
21 | export function unlockLogininfor(userName) {
22 | return request({
23 | url: '/monitor/logininfor/unlock/' + userName,
24 | method: 'get'
25 | })
26 | }
27 |
28 | // 清空登录日志
29 | export function cleanLogininfor() {
30 | return request({
31 | url: '/monitor/logininfor/clean',
32 | method: 'delete'
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/layout/components/Copyright/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
15 |
16 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/textarea.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/layout/components/IframeToggle/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/time.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/layout/components/Sidebar/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
41 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/iFrame/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
32 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/dict.js:
--------------------------------------------------------------------------------
1 | import useDictStore from '@/store/modules/dict'
2 | import { getDicts } from '@/api/system/dict/data'
3 |
4 | /**
5 | * 获取字典数据
6 | */
7 | export function useDict(...args) {
8 | const res = ref({});
9 | return (() => {
10 | args.forEach((dictType, index) => {
11 | res.value[dictType] = [];
12 | const dicts = useDictStore().getDict(dictType);
13 | if (dicts) {
14 | res.value[dictType] = dicts;
15 | } else {
16 | getDicts(dictType).then(resp => {
17 | res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
18 | useDictStore().setDict(dictType, res.value[dictType]);
19 | })
20 | }
21 | })
22 | return toRefs(res.value);
23 | })()
24 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/edit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/directive/permission/hasPermi.js:
--------------------------------------------------------------------------------
1 | /**
2 | * v-hasPermi 操作权限处理
3 | * Copyright (c) 2019 ruoyi
4 | */
5 | import useUserStore from '@/store/modules/user'
6 |
7 | export default {
8 | mounted(el, binding, vnode) {
9 | const { value } = binding
10 | const all_permission = "*:*:*";
11 | const permissions = useUserStore().permissions
12 |
13 | if (value && value instanceof Array && value.length > 0) {
14 | const permissionFlag = value
15 |
16 | const hasPermissions = permissions.some(permission => {
17 | return all_permission === permission || permissionFlag.includes(permission)
18 | })
19 |
20 | if (!hasPermissions) {
21 | el.parentNode && el.parentNode.removeChild(el)
22 | }
23 | } else {
24 | throw new Error(`请设置操作权限标签值`)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/nested.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/alembic/script.py.mako:
--------------------------------------------------------------------------------
1 | """${message}
2 |
3 | Revision ID: ${up_revision}
4 | Revises: ${down_revision | comma,n}
5 | Create Date: ${create_date}
6 |
7 | """
8 | from typing import Sequence, Union
9 |
10 | from alembic import op
11 | import sqlalchemy as sa
12 | ${imports if imports else ""}
13 |
14 | # revision identifiers, used by Alembic.
15 | revision: str = ${repr(up_revision)}
16 | down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
17 | branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18 | depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19 |
20 |
21 | def upgrade() -> None:
22 | """Upgrade schema."""
23 | ${upgrades if upgrades else "pass"}
24 |
25 |
26 | def downgrade() -> None:
27 | """Downgrade schema."""
28 | ${downgrades if downgrades else "pass"}
29 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/layout/components/InnerLink/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
36 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/row.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/context_middleware.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI, Request
2 | from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
3 | from starlette.responses import Response
4 |
5 | from common.context import RequestContext
6 |
7 |
8 | class ContextCleanupMiddleware(BaseHTTPMiddleware):
9 | """
10 | 上下文清理中间件
11 | """
12 |
13 | async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
14 | """
15 | 在每个请求处理完成后清理上下文信息
16 | """
17 | response = await call_next(request)
18 | # 请求处理完成后清理所有上下文变量
19 | RequestContext.clear_all()
20 | return response
21 |
22 |
23 | def add_context_cleanup_middleware(app: FastAPI) -> None:
24 | """
25 | 添加上下文清理中间件
26 |
27 | :param app: FastAPI对象
28 | """
29 | app.add_middleware(ContextCleanupMiddleware)
30 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/utils/pwd_util.py:
--------------------------------------------------------------------------------
1 | import bcrypt
2 |
3 |
4 | class PwdUtil:
5 | """
6 | 密码工具类
7 | """
8 |
9 | @classmethod
10 | def verify_password(cls, plain_password: str, hashed_password: str) -> bool:
11 | """
12 | 工具方法:校验当前输入的密码与数据库存储的密码是否一致
13 |
14 | :param plain_password: 当前输入的密码
15 | :param hashed_password: 数据库存储的密码
16 | :return: 校验结果
17 | """
18 | return (
19 | bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8')) if hashed_password else None
20 | )
21 |
22 | @classmethod
23 | def get_password_hash(cls, input_password: str) -> str:
24 | """
25 | 工具方法:对当前输入的密码进行加密
26 |
27 | :param input_password: 输入的密码
28 | :return: 加密成功的密码
29 | """
30 | return bcrypt.hashpw(input_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
31 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/monitor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/post.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询岗位列表
4 | export function listPost(query) {
5 | return request({
6 | url: '/system/post/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询岗位详细
13 | export function getPost(postId) {
14 | return request({
15 | url: '/system/post/' + postId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 新增岗位
21 | export function addPost(data) {
22 | return request({
23 | url: '/system/post',
24 | method: 'post',
25 | data: data
26 | })
27 | }
28 |
29 | // 修改岗位
30 | export function updatePost(data) {
31 | return request({
32 | url: '/system/post',
33 | method: 'put',
34 | data: data
35 | })
36 | }
37 |
38 | // 删除岗位
39 | export function delPost(postId) {
40 | return request({
41 | url: '/system/post/' + postId,
42 | method: 'delete'
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/tree-table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/notice.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询公告列表
4 | export function listNotice(query) {
5 | return request({
6 | url: '/system/notice/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询公告详细
13 | export function getNotice(noticeId) {
14 | return request({
15 | url: '/system/notice/' + noticeId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 新增公告
21 | export function addNotice(data) {
22 | return request({
23 | url: '/system/notice',
24 | method: 'post',
25 | data: data
26 | })
27 | }
28 |
29 | // 修改公告
30 | export function updateNotice(data) {
31 | return request({
32 | url: '/system/notice',
33 | method: 'put',
34 | data: data
35 | })
36 | }
37 |
38 | // 删除公告
39 | export function delNotice(noticeId) {
40 | return request({
41 | url: '/system/notice/' + noticeId,
42 | method: 'delete'
43 | })
44 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/settings.js:
--------------------------------------------------------------------------------
1 | export default {
2 | /**
3 | * 网页标题
4 | */
5 | title: import.meta.env.VITE_APP_TITLE,
6 |
7 | /**
8 | * 侧边栏主题 深色主题theme-dark,浅色主题theme-light
9 | */
10 | sideTheme: 'theme-dark',
11 |
12 | /**
13 | * 是否系统布局配置
14 | */
15 | showSettings: true,
16 |
17 | /**
18 | * 菜单导航模式 1、纯左侧 2、混合(左侧+顶部) 3、纯顶部
19 | */
20 | navType: 1,
21 |
22 | /**
23 | * 是否显示 tagsView
24 | */
25 | tagsView: true,
26 |
27 | /**
28 | * 显示页签图标
29 | */
30 | tagsIcon: false,
31 |
32 | /**
33 | * 是否固定头部
34 | */
35 | fixedHeader: true,
36 |
37 | /**
38 | * 是否显示logo
39 | */
40 | sidebarLogo: true,
41 |
42 | /**
43 | * 是否显示动态标题
44 | */
45 | dynamicTitle: false,
46 |
47 | /**
48 | * 是否显示底部版权
49 | */
50 | footerVisible: false,
51 |
52 | /**
53 | * 底部版权文本内容
54 | */
55 | footerContent: 'Copyright © 2024-2025 insistence.tech All Rights Reserved.'
56 | }
57 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/eye.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/build.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/clipboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/vite/plugins/compression.js:
--------------------------------------------------------------------------------
1 | import compression from 'vite-plugin-compression'
2 |
3 | export default function createCompression(env) {
4 | const { VITE_BUILD_COMPRESS } = env
5 | const plugin = []
6 | if (VITE_BUILD_COMPRESS) {
7 | const compressList = VITE_BUILD_COMPRESS.split(',')
8 | if (compressList.includes('gzip')) {
9 | // http://doc.ruoyi.vip/ruoyi-vue/other/faq.html#使用gzip解压缩静态文件
10 | plugin.push(
11 | compression({
12 | ext: '.gz',
13 | deleteOriginFile: false
14 | })
15 | )
16 | }
17 | if (compressList.includes('brotli')) {
18 | plugin.push(
19 | compression({
20 | ext: '.br',
21 | algorithm: 'brotliCompress',
22 | deleteOriginFile: false
23 | })
24 | )
25 | }
26 | }
27 | return plugin
28 | }
29 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/dao/login_dao.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 |
3 | from sqlalchemy import Row, and_, select
4 | from sqlalchemy.ext.asyncio import AsyncSession
5 |
6 | from module_admin.entity.do.dept_do import SysDept
7 | from module_admin.entity.do.user_do import SysUser
8 |
9 |
10 | async def login_by_account(db: AsyncSession, user_name: str) -> Union[Row[tuple[SysUser, SysDept]], None]:
11 | """
12 | 根据用户名查询用户信息
13 |
14 | :param db: orm对象
15 | :param user_name: 用户名
16 | :return: 用户对象
17 | """
18 | user = (
19 | await db.execute(
20 | select(SysUser, SysDept)
21 | .where(SysUser.user_name == user_name, SysUser.del_flag == '0')
22 | .join(
23 | SysDept,
24 | and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == '0', SysDept.del_flag == '0'),
25 | isouter=True,
26 | )
27 | .distinct()
28 | )
29 | ).first()
30 |
31 | return user
32 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/styles/transition.scss:
--------------------------------------------------------------------------------
1 | // global transition css
2 |
3 | /* fade */
4 | .fade-enter-active,
5 | .fade-leave-active {
6 | transition: opacity 0.28s;
7 | }
8 |
9 | .fade-enter-from,
10 | .fade-leave-active {
11 | opacity: 0;
12 | }
13 |
14 | /* fade-transform */
15 | .fade-transform--move,
16 | .fade-transform-leave-active,
17 | .fade-transform-enter-active {
18 | transition: all .5s;
19 | }
20 |
21 | .fade-transform-enter-from {
22 | opacity: 0;
23 | transform: translateX(-30px);
24 | }
25 |
26 | .fade-transform-leave-to {
27 | opacity: 0;
28 | transform: translateX(30px);
29 | }
30 |
31 | /* breadcrumb transition */
32 | .breadcrumb-enter-active,
33 | .breadcrumb-leave-active {
34 | transition: all .5s;
35 | }
36 |
37 | .breadcrumb-enter-from,
38 | .breadcrumb-leave-active {
39 | opacity: 0;
40 | transform: translateX(20px);
41 | }
42 |
43 | .breadcrumb-move {
44 | transition: all .5s;
45 | }
46 |
47 | .breadcrumb-leave-active {
48 | position: absolute;
49 | }
50 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/list.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/vo/cache_vo.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Optional
2 |
3 | from pydantic import BaseModel, ConfigDict, Field
4 | from pydantic.alias_generators import to_camel
5 |
6 |
7 | class CacheMonitorModel(BaseModel):
8 | """
9 | 缓存监控信息对应pydantic模型
10 | """
11 |
12 | model_config = ConfigDict(alias_generator=to_camel)
13 |
14 | command_stats: Optional[list] = Field(default=[], description='命令统计')
15 | db_size: Optional[int] = Field(default=None, description='Key数量')
16 | info: Optional[dict] = Field(default={}, description='Redis信息')
17 |
18 |
19 | class CacheInfoModel(BaseModel):
20 | """
21 | 缓存监控对象对应pydantic模型
22 | """
23 |
24 | model_config = ConfigDict(alias_generator=to_camel)
25 |
26 | cache_key: Optional[str] = Field(default=None, description='缓存键名')
27 | cache_name: Optional[str] = Field(default=None, description='缓存名称')
28 | cache_value: Optional[Any] = Field(default=None, description='缓存内容')
29 | remark: Optional[str] = Field(default=None, description='备注')
30 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/generator/drawingDefault.js:
--------------------------------------------------------------------------------
1 | export const drawingDefaultValue = []
2 |
3 | export function initDrawingDefaultValue() {
4 | if (drawingDefaultValue.length === 0) {
5 | drawingDefaultValue.push({
6 | layout: 'colFormItem',
7 | tagIcon: 'input',
8 | label: '手机号',
9 | vModel: 'mobile',
10 | formId: 6,
11 | tag: 'el-input',
12 | placeholder: '请输入手机号',
13 | defaultValue: '',
14 | span: 24,
15 | style: {width: '100%'},
16 | clearable: true,
17 | prepend: '',
18 | append: '',
19 | 'prefix-icon': 'Cellphone',
20 | 'suffix-icon': '',
21 | maxlength: 11,
22 | 'show-word-limit': true,
23 | readonly: false,
24 | disabled: false,
25 | required: true,
26 | changeTag: true,
27 | regList: [{
28 | pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
29 | message: '手机号格式错误'
30 | }]
31 | })
32 | }
33 | }
34 |
35 | export function cleanDrawingDefaultValue() {
36 | drawingDefaultValue.splice(0, drawingDefaultValue.length)
37 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/dept.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询部门列表
4 | export function listDept(query) {
5 | return request({
6 | url: '/system/dept/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询部门列表(排除节点)
13 | export function listDeptExcludeChild(deptId) {
14 | return request({
15 | url: '/system/dept/list/exclude/' + deptId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 查询部门详细
21 | export function getDept(deptId) {
22 | return request({
23 | url: '/system/dept/' + deptId,
24 | method: 'get'
25 | })
26 | }
27 |
28 | // 新增部门
29 | export function addDept(data) {
30 | return request({
31 | url: '/system/dept',
32 | method: 'post',
33 | data: data
34 | })
35 | }
36 |
37 | // 修改部门
38 | export function updateDept(data) {
39 | return request({
40 | url: '/system/dept',
41 | method: 'put',
42 | data: data
43 | })
44 | }
45 |
46 | // 删除部门
47 | export function delDept(deptId) {
48 | return request({
49 | url: '/system/dept/' + deptId,
50 | method: 'delete'
51 | })
52 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/enter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/international.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 insistence
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/dict/data.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询字典数据列表
4 | export function listData(query) {
5 | return request({
6 | url: '/system/dict/data/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询字典数据详细
13 | export function getData(dictCode) {
14 | return request({
15 | url: '/system/dict/data/' + dictCode,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 根据字典类型查询字典数据信息
21 | export function getDicts(dictType) {
22 | return request({
23 | url: '/system/dict/data/type/' + dictType,
24 | method: 'get'
25 | })
26 | }
27 |
28 | // 新增字典数据
29 | export function addData(data) {
30 | return request({
31 | url: '/system/dict/data',
32 | method: 'post',
33 | data: data
34 | })
35 | }
36 |
37 | // 修改字典数据
38 | export function updateData(data) {
39 | return request({
40 | url: '/system/dict/data',
41 | method: 'put',
42 | data: data
43 | })
44 | }
45 |
46 | // 删除字典数据
47 | export function delData(dictCode) {
48 | return request({
49 | url: '/system/dict/data/' + dictCode,
50 | method: 'delete'
51 | })
52 | }
53 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/more-up.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/question.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 RuoYi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/wechat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
36 |
37 |
54 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/skill.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/controller/server_controller.py:
--------------------------------------------------------------------------------
1 | from fastapi import Request, Response
2 |
3 | from common.aspect.interface_auth import UserInterfaceAuthDependency
4 | from common.aspect.pre_auth import PreAuthDependency
5 | from common.router import APIRouterPro
6 | from common.vo import DataResponseModel
7 | from module_admin.entity.vo.server_vo import ServerMonitorModel
8 | from module_admin.service.server_service import ServerService
9 | from utils.log_util import logger
10 | from utils.response_util import ResponseUtil
11 |
12 | server_controller = APIRouterPro(
13 | prefix='/monitor/server', order_num=14, tags=['系统监控-服务监控'], dependencies=[PreAuthDependency()]
14 | )
15 |
16 |
17 | @server_controller.get(
18 | '',
19 | summary='获取服务器监控信息接口',
20 | description='用于获取当前服务器的监控信息',
21 | response_model=DataResponseModel[ServerMonitorModel],
22 | dependencies=[UserInterfaceAuthDependency('monitor:server:list')],
23 | )
24 | async def get_monitor_server_info(request: Request) -> Response:
25 | # 获取全量数据
26 | server_info_query_result = await ServerService.get_server_monitor_info()
27 | logger.info('获取成功')
28 |
29 | return ResponseUtil.success(data=server_info_query_result)
30 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/people.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/login.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 登录方法
4 | export function login(username, password, code, uuid) {
5 | const data = {
6 | username,
7 | password,
8 | code,
9 | uuid
10 | }
11 | return request({
12 | url: '/login',
13 | headers: {
14 | isToken: false,
15 | repeatSubmit: false,
16 | 'Content-Type': 'application/x-www-form-urlencoded'
17 | },
18 | method: 'post',
19 | data: data
20 | })
21 | }
22 |
23 | // 注册方法
24 | export function register(data) {
25 | return request({
26 | url: '/register',
27 | headers: {
28 | isToken: false
29 | },
30 | method: 'post',
31 | data: data
32 | })
33 | }
34 |
35 | // 获取用户详细信息
36 | export function getInfo() {
37 | return request({
38 | url: '/getInfo',
39 | method: 'get'
40 | })
41 | }
42 |
43 | // 退出方法
44 | export function logout() {
45 | return request({
46 | url: '/logout',
47 | method: 'post'
48 | })
49 | }
50 |
51 | // 获取验证码
52 | export function getCodeImg() {
53 | return request({
54 | url: '/captchaImage',
55 | headers: {
56 | isToken: false
57 | },
58 | method: 'get',
59 | timeout: 20000
60 | })
61 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/post.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/menu.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询菜单列表
4 | export function listMenu(query) {
5 | return request({
6 | url: '/system/menu/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询菜单详细
13 | export function getMenu(menuId) {
14 | return request({
15 | url: '/system/menu/' + menuId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 查询菜单下拉树结构
21 | export function treeselect() {
22 | return request({
23 | url: '/system/menu/treeselect',
24 | method: 'get'
25 | })
26 | }
27 |
28 | // 根据角色ID查询菜单下拉树结构
29 | export function roleMenuTreeselect(roleId) {
30 | return request({
31 | url: '/system/menu/roleMenuTreeselect/' + roleId,
32 | method: 'get'
33 | })
34 | }
35 |
36 | // 新增菜单
37 | export function addMenu(data) {
38 | return request({
39 | url: '/system/menu',
40 | method: 'post',
41 | data: data
42 | })
43 | }
44 |
45 | // 修改菜单
46 | export function updateMenu(data) {
47 | return request({
48 | url: '/system/menu',
49 | method: 'put',
50 | data: data
51 | })
52 | }
53 |
54 | // 删除菜单
55 | export function delMenu(menuId) {
56 | return request({
57 | url: '/system/menu/' + menuId,
58 | method: 'delete'
59 | })
60 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/checkbox.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/language.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_generator/templates/js/api.js.jinja2:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询{{ functionName }}列表
4 | export function list{{ BusinessName }}(query) {
5 | return request({
6 | url: '/{{ moduleName }}/{{ businessName }}/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询{{ functionName }}详细
13 | export function get{{ BusinessName }}({{ pkColumn.python_field }}) {
14 | return request({
15 | url: '/{{ moduleName }}/{{ businessName }}/' + {{ pkColumn.python_field }},
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 新增{{ functionName }}
21 | export function add{{ BusinessName }}(data) {
22 | return request({
23 | url: '/{{ moduleName }}/{{ businessName }}',
24 | method: 'post',
25 | data: data
26 | })
27 | }
28 |
29 | // 修改{{ functionName }}
30 | export function update{{ BusinessName }}(data) {
31 | return request({
32 | url: '/{{ moduleName }}/{{ businessName }}',
33 | method: 'put',
34 | data: data
35 | })
36 | }
37 |
38 | // 删除{{ functionName }}
39 | export function del{{ BusinessName }}({{ pkColumn.python_field }}) {
40 | return request({
41 | url: '/{{ moduleName }}/{{ businessName }}/' + {{ pkColumn.python_field }},
42 | method: 'delete'
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/jsencrypt.js:
--------------------------------------------------------------------------------
1 | import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
2 |
3 | // 密钥对生成 http://web.chacuo.net/netrsakeypair
4 |
5 | const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
6 | 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
7 |
8 | const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
9 | '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
10 | 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
11 | 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
12 | 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
13 | 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
14 | 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
15 | 'UP8iWi1Qw0Y='
16 |
17 | // 加密
18 | export function encrypt(txt) {
19 | const encryptor = new JSEncrypt()
20 | encryptor.setPublicKey(publicKey) // 设置公钥
21 | return encryptor.encrypt(txt) // 对数据进行加密
22 | }
23 |
24 | // 解密
25 | export function decrypt(txt) {
26 | const encryptor = new JSEncrypt()
27 | encryptor.setPrivateKey(privateKey) // 设置私钥
28 | return encryptor.decrypt(txt) // 对数据进行解密
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/config/database.py:
--------------------------------------------------------------------------------
1 | from urllib.parse import quote_plus
2 |
3 | from sqlalchemy.ext.asyncio import AsyncAttrs, async_sessionmaker, create_async_engine
4 | from sqlalchemy.orm import DeclarativeBase
5 |
6 | from config.env import DataBaseConfig
7 |
8 | ASYNC_SQLALCHEMY_DATABASE_URL = (
9 | f'mysql+asyncmy://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
10 | f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
11 | )
12 | if DataBaseConfig.db_type == 'postgresql':
13 | ASYNC_SQLALCHEMY_DATABASE_URL = (
14 | f'postgresql+asyncpg://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@'
15 | f'{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}'
16 | )
17 |
18 | async_engine = create_async_engine(
19 | ASYNC_SQLALCHEMY_DATABASE_URL,
20 | echo=DataBaseConfig.db_echo,
21 | max_overflow=DataBaseConfig.db_max_overflow,
22 | pool_size=DataBaseConfig.db_pool_size,
23 | pool_recycle=DataBaseConfig.db_pool_recycle,
24 | pool_timeout=DataBaseConfig.db_pool_timeout,
25 | )
26 | AsyncSessionLocal = async_sessionmaker(autocommit=False, autoflush=False, bind=async_engine)
27 |
28 |
29 | class Base(AsyncAttrs, DeclarativeBase):
30 | pass
31 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/dict/type.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询字典类型列表
4 | export function listType(query) {
5 | return request({
6 | url: '/system/dict/type/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询字典类型详细
13 | export function getType(dictId) {
14 | return request({
15 | url: '/system/dict/type/' + dictId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 新增字典类型
21 | export function addType(data) {
22 | return request({
23 | url: '/system/dict/type',
24 | method: 'post',
25 | data: data
26 | })
27 | }
28 |
29 | // 修改字典类型
30 | export function updateType(data) {
31 | return request({
32 | url: '/system/dict/type',
33 | method: 'put',
34 | data: data
35 | })
36 | }
37 |
38 | // 删除字典类型
39 | export function delType(dictId) {
40 | return request({
41 | url: '/system/dict/type/' + dictId,
42 | method: 'delete'
43 | })
44 | }
45 |
46 | // 刷新字典缓存
47 | export function refreshCache() {
48 | return request({
49 | url: '/system/dict/type/refreshCache',
50 | method: 'delete'
51 | })
52 | }
53 |
54 | // 获取字典选择框列表
55 | export function optionselect() {
56 | return request({
57 | url: '/system/dict/type/optionselect',
58 | method: 'get'
59 | })
60 | }
61 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/system/config.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询参数列表
4 | export function listConfig(query) {
5 | return request({
6 | url: '/system/config/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询参数详细
13 | export function getConfig(configId) {
14 | return request({
15 | url: '/system/config/' + configId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 根据参数键名查询参数值
21 | export function getConfigKey(configKey) {
22 | return request({
23 | url: '/system/config/configKey/' + configKey,
24 | method: 'get'
25 | })
26 | }
27 |
28 | // 新增参数配置
29 | export function addConfig(data) {
30 | return request({
31 | url: '/system/config',
32 | method: 'post',
33 | data: data
34 | })
35 | }
36 |
37 | // 修改参数配置
38 | export function updateConfig(data) {
39 | return request({
40 | url: '/system/config',
41 | method: 'put',
42 | data: data
43 | })
44 | }
45 |
46 | // 删除参数配置
47 | export function delConfig(configId) {
48 | return request({
49 | url: '/system/config/' + configId,
50 | method: 'delete'
51 | })
52 | }
53 |
54 | // 刷新参数缓存
55 | export function refreshCache() {
56 | return request({
57 | url: '/system/config/refreshCache',
58 | method: 'delete'
59 | })
60 | }
61 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/eye-open.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/tool/gen/createTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 创建表语句(支持多个建表语句):
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
47 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/cache.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询缓存详细
4 | export function getCache() {
5 | return request({
6 | url: '/monitor/cache',
7 | method: 'get'
8 | })
9 | }
10 |
11 | // 查询缓存名称列表
12 | export function listCacheName() {
13 | return request({
14 | url: '/monitor/cache/getNames',
15 | method: 'get'
16 | })
17 | }
18 |
19 | // 查询缓存键名列表
20 | export function listCacheKey(cacheName) {
21 | return request({
22 | url: '/monitor/cache/getKeys/' + cacheName,
23 | method: 'get'
24 | })
25 | }
26 |
27 | // 查询缓存内容
28 | export function getCacheValue(cacheName, cacheKey) {
29 | return request({
30 | url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey,
31 | method: 'get'
32 | })
33 | }
34 |
35 | // 清理指定名称缓存
36 | export function clearCacheName(cacheName) {
37 | return request({
38 | url: '/monitor/cache/clearCacheName/' + cacheName,
39 | method: 'delete'
40 | })
41 | }
42 |
43 | // 清理指定键名缓存
44 | export function clearCacheKey(cacheKey) {
45 | return request({
46 | url: '/monitor/cache/clearCacheKey/' + cacheKey,
47 | method: 'delete'
48 | })
49 | }
50 |
51 | // 清理全部缓存
52 | export function clearCacheAll() {
53 | return request({
54 | url: '/monitor/cache/clearCacheAll',
55 | method: 'delete'
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/common/enums.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 | from typing import Union
3 |
4 |
5 | class BusinessType(Enum):
6 | """
7 | 业务操作类型
8 |
9 | OTHER: 其它
10 | INSERT: 新增
11 | UPDATE: 修改
12 | DELETE: 删除
13 | GRANT: 授权
14 | EXPORT: 导出
15 | IMPORT: 导入
16 | FORCE: 强退
17 | GENCODE: 生成代码
18 | CLEAN: 清空数据
19 | """
20 |
21 | OTHER = 0
22 | INSERT = 1
23 | UPDATE = 2
24 | DELETE = 3
25 | GRANT = 4
26 | EXPORT = 5
27 | IMPORT = 6
28 | FORCE = 7
29 | GENCODE = 8
30 | CLEAN = 9
31 |
32 |
33 | class RedisInitKeyConfig(Enum):
34 | """
35 | 系统内置Redis键名
36 | """
37 |
38 | @property
39 | def key(self) -> Union[str, None]:
40 | return self.value.get('key')
41 |
42 | @property
43 | def remark(self) -> Union[str, None]:
44 | return self.value.get('remark')
45 |
46 | ACCESS_TOKEN = {'key': 'access_token', 'remark': '登录令牌信息'}
47 | SYS_DICT = {'key': 'sys_dict', 'remark': '数据字典'}
48 | SYS_CONFIG = {'key': 'sys_config', 'remark': '配置信息'}
49 | CAPTCHA_CODES = {'key': 'captcha_codes', 'remark': '图片验证码'}
50 | ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'}
51 | PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'}
52 | SMS_CODE = {'key': 'sms_code', 'remark': '短信验证码'}
53 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/validCode.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/dashboard/editable-link-group.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
21 |
22 |
52 |
53 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/radio.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
30 |
31 |
43 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/select.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/upload.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/components/SizeSelect/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ item.label }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
38 |
39 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/do/post_do.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
4 |
5 | from config.database import Base
6 | from config.env import DataBaseConfig
7 | from utils.common_util import SqlalchemyUtil
8 |
9 |
10 | class SysPost(Base):
11 | """
12 | 岗位信息表
13 | """
14 |
15 | __tablename__ = 'sys_post'
16 | __table_args__ = {'comment': '岗位信息表'}
17 |
18 | post_id = Column(BigInteger, primary_key=True, nullable=False, autoincrement=True, comment='岗位ID')
19 | post_code = Column(String(64), nullable=False, comment='岗位编码')
20 | post_name = Column(String(50), nullable=False, comment='岗位名称')
21 | post_sort = Column(Integer, nullable=False, comment='显示顺序')
22 | status = Column(CHAR(1), nullable=False, comment='状态(0正常 1停用)')
23 | create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
24 | create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
25 | update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
26 | update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
27 | remark = Column(
28 | String(500),
29 | nullable=True,
30 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
31 | comment='备注',
32 | )
33 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const useAppStore = defineStore(
4 | 'app',
5 | {
6 | state: () => ({
7 | sidebar: {
8 | opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
9 | withoutAnimation: false,
10 | hide: false
11 | },
12 | device: 'desktop',
13 | size: Cookies.get('size') || 'default'
14 | }),
15 | actions: {
16 | toggleSideBar(withoutAnimation) {
17 | if (this.sidebar.hide) {
18 | return false;
19 | }
20 | this.sidebar.opened = !this.sidebar.opened
21 | this.sidebar.withoutAnimation = withoutAnimation
22 | if (this.sidebar.opened) {
23 | Cookies.set('sidebarStatus', 1)
24 | } else {
25 | Cookies.set('sidebarStatus', 0)
26 | }
27 | },
28 | closeSideBar({ withoutAnimation }) {
29 | Cookies.set('sidebarStatus', 0)
30 | this.sidebar.opened = false
31 | this.sidebar.withoutAnimation = withoutAnimation
32 | },
33 | toggleDevice(device) {
34 | this.device = device
35 | },
36 | setSize(size) {
37 | this.size = size;
38 | Cookies.set('size', size)
39 | },
40 | toggleSideBarHide(status) {
41 | this.sidebar.hide = status
42 | }
43 | }
44 | })
45 |
46 | export default useAppStore
47 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/sunny.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/monitor/job.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询定时任务调度列表
4 | export function listJob(query) {
5 | return request({
6 | url: '/monitor/job/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 |
12 | // 查询定时任务调度详细
13 | export function getJob(jobId) {
14 | return request({
15 | url: '/monitor/job/' + jobId,
16 | method: 'get'
17 | })
18 | }
19 |
20 | // 新增定时任务调度
21 | export function addJob(data) {
22 | return request({
23 | url: '/monitor/job',
24 | method: 'post',
25 | data: data
26 | })
27 | }
28 |
29 | // 修改定时任务调度
30 | export function updateJob(data) {
31 | return request({
32 | url: '/monitor/job',
33 | method: 'put',
34 | data: data
35 | })
36 | }
37 |
38 | // 删除定时任务调度
39 | export function delJob(jobId) {
40 | return request({
41 | url: '/monitor/job/' + jobId,
42 | method: 'delete'
43 | })
44 | }
45 |
46 | // 任务状态修改
47 | export function changeJobStatus(jobId, status) {
48 | const data = {
49 | jobId,
50 | status
51 | }
52 | return request({
53 | url: '/monitor/job/changeStatus',
54 | method: 'put',
55 | data: data
56 | })
57 | }
58 |
59 |
60 | // 定时任务立即执行一次
61 | export function runJob(jobId, jobGroup) {
62 | const data = {
63 | jobId,
64 | jobGroup
65 | }
66 | return request({
67 | url: '/monitor/job/run',
68 | method: 'put',
69 | data: data
70 | })
71 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/404.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/trace_middleware/middle.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 |
3 | from starlette.types import ASGIApp, Message, Receive, Scope, Send
4 |
5 | from .span import Span, get_current_span
6 |
7 |
8 | class TraceASGIMiddleware:
9 | """
10 | fastapi-example:
11 | app = FastAPI()
12 | app.add_middleware(TraceASGIMiddleware)
13 | """
14 |
15 | def __init__(self, app: ASGIApp) -> None:
16 | self.app = app
17 |
18 | @staticmethod
19 | async def my_receive(receive: Receive, span: Span) -> Receive:
20 | await span.request_before()
21 |
22 | @wraps(receive)
23 | async def my_receive() -> Message:
24 | message = await receive()
25 | await span.request_after(message)
26 | return message
27 |
28 | return my_receive
29 |
30 | async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
31 | if scope['type'] != 'http':
32 | await self.app(scope, receive, send)
33 | return
34 |
35 | async with get_current_span(scope) as span:
36 | handle_outgoing_receive = await self.my_receive(receive, span)
37 |
38 | async def handle_outgoing_request(message: 'Message') -> None:
39 | await span.response(message)
40 | await send(message)
41 |
42 | await self.app(scope, handle_outgoing_receive, handle_outgoing_request)
43 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/store/modules/dict.js:
--------------------------------------------------------------------------------
1 | const useDictStore = defineStore(
2 | 'dict',
3 | {
4 | state: () => ({
5 | dict: new Array()
6 | }),
7 | actions: {
8 | // 获取字典
9 | getDict(_key) {
10 | if (_key == null && _key == "") {
11 | return null;
12 | }
13 | try {
14 | for (let i = 0; i < this.dict.length; i++) {
15 | if (this.dict[i].key == _key) {
16 | return this.dict[i].value;
17 | }
18 | }
19 | } catch (e) {
20 | return null;
21 | }
22 | },
23 | // 设置字典
24 | setDict(_key, value) {
25 | if (_key !== null && _key !== "") {
26 | this.dict.push({
27 | key: _key,
28 | value: value
29 | });
30 | }
31 | },
32 | // 删除字典
33 | removeDict(_key) {
34 | var bln = false;
35 | try {
36 | for (let i = 0; i < this.dict.length; i++) {
37 | if (this.dict[i].key == _key) {
38 | this.dict.splice(i, 1);
39 | return true;
40 | }
41 | }
42 | } catch (e) {
43 | bln = false;
44 | }
45 | return bln;
46 | },
47 | // 清空字典
48 | cleanDict() {
49 | this.dict = new Array();
50 | },
51 | // 初始字典
52 | initDict() {
53 | }
54 | }
55 | })
56 |
57 | export default useDictStore
58 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/zip.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/.env.dev:
--------------------------------------------------------------------------------
1 | # -------- 应用配置 --------
2 | # 应用运行环境
3 | APP_ENV = 'dev'
4 | # 应用名称
5 | APP_NAME = 'RuoYi-FastAPI'
6 | # 应用代理路径
7 | APP_ROOT_PATH = '/dev-api'
8 | # 应用主机
9 | APP_HOST = '0.0.0.0'
10 | # 应用端口
11 | APP_PORT = 9099
12 | # 应用版本
13 | APP_VERSION= '1.8.0'
14 | # 应用是否开启热重载
15 | APP_RELOAD = true
16 | # 应用是否开启IP归属区域查询
17 | APP_IP_LOCATION_QUERY = true
18 | # 应用是否允许账号同时登录
19 | APP_SAME_TIME_LOGIN = true
20 |
21 | # -------- Jwt配置 --------
22 | # Jwt秘钥
23 | JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
24 | # Jwt算法
25 | JWT_ALGORITHM = 'HS256'
26 | # 令牌过期时间
27 | JWT_EXPIRE_MINUTES = 1440
28 | # redis中令牌过期时间
29 | JWT_REDIS_EXPIRE_MINUTES = 30
30 |
31 |
32 | # -------- 数据库配置 --------
33 | # 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
34 | DB_TYPE = 'mysql'
35 | # 数据库主机
36 | DB_HOST = '127.0.0.1'
37 | # 数据库端口
38 | DB_PORT = 3306
39 | # 数据库用户名
40 | DB_USERNAME = 'root'
41 | # 数据库密码
42 | DB_PASSWORD = 'mysqlroot'
43 | # 数据库名称
44 | DB_DATABASE = 'ruoyi-fastapi'
45 | # 是否开启sqlalchemy日志
46 | DB_ECHO = true
47 | # 允许溢出连接池大小的最大连接数
48 | DB_MAX_OVERFLOW = 10
49 | # 连接池大小,0表示连接数无限制
50 | DB_POOL_SIZE = 50
51 | # 连接回收时间(单位:秒)
52 | DB_POOL_RECYCLE = 3600
53 | # 连接池中没有线程可用时,最多等待的时间(单位:秒)
54 | DB_POOL_TIMEOUT = 30
55 |
56 | # -------- Redis配置 --------
57 | # Redis主机
58 | REDIS_HOST = '127.0.0.1'
59 | # Redis端口
60 | REDIS_PORT = 6379
61 | # Redis用户名
62 | REDIS_USERNAME = ''
63 | # Redis密码
64 | REDIS_PASSWORD = ''
65 | # Redis数据库
66 | REDIS_DATABASE = 2
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/.env.prod:
--------------------------------------------------------------------------------
1 | # -------- 应用配置 --------
2 | # 应用运行环境
3 | APP_ENV = 'prod'
4 | # 应用名称
5 | APP_NAME = 'RuoYi-FastAPI'
6 | # 应用代理路径
7 | APP_ROOT_PATH = '/prod-api'
8 | # 应用主机
9 | APP_HOST = '0.0.0.0'
10 | # 应用端口
11 | APP_PORT = 9099
12 | # 应用版本
13 | APP_VERSION= '1.8.0'
14 | # 应用是否开启热重载
15 | APP_RELOAD = false
16 | # 应用是否开启IP归属区域查询
17 | APP_IP_LOCATION_QUERY = true
18 | # 应用是否允许账号同时登录
19 | APP_SAME_TIME_LOGIN = true
20 |
21 | # -------- Jwt配置 --------
22 | # Jwt秘钥
23 | JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
24 | # Jwt算法
25 | JWT_ALGORITHM = 'HS256'
26 | # 令牌过期时间
27 | JWT_EXPIRE_MINUTES = 1440
28 | # redis中令牌过期时间
29 | JWT_REDIS_EXPIRE_MINUTES = 30
30 |
31 |
32 | # -------- 数据库配置 --------
33 | # 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
34 | DB_TYPE = 'mysql'
35 | # 数据库主机
36 | DB_HOST = '127.0.0.1'
37 | # 数据库端口
38 | DB_PORT = 3306
39 | # 数据库用户名
40 | DB_USERNAME = 'root'
41 | # 数据库密码
42 | DB_PASSWORD = 'root'
43 | # 数据库名称
44 | DB_DATABASE = 'ruoyi-fastapi'
45 | # 是否开启sqlalchemy日志
46 | DB_ECHO = true
47 | # 允许溢出连接池大小的最大连接数
48 | DB_MAX_OVERFLOW = 10
49 | # 连接池大小,0表示连接数无限制
50 | DB_POOL_SIZE = 50
51 | # 连接回收时间(单位:秒)
52 | DB_POOL_RECYCLE = 3600
53 | # 连接池中没有线程可用时,最多等待的时间(单位:秒)
54 | DB_POOL_TIMEOUT = 30
55 |
56 | # -------- Redis配置 --------
57 | # Redis主机
58 | REDIS_HOST = '127.0.0.1'
59 | # Redis端口
60 | REDIS_PORT = 6379
61 | # Redis用户名
62 | REDIS_USERNAME = ''
63 | # Redis密码
64 | REDIS_PASSWORD = ''
65 | # Redis数据库
66 | REDIS_DATABASE = 2
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/permission.js:
--------------------------------------------------------------------------------
1 | import useUserStore from '@/store/modules/user'
2 |
3 | /**
4 | * 字符权限校验
5 | * @param {Array} value 校验值
6 | * @returns {Boolean}
7 | */
8 | export function checkPermi(value) {
9 | if (value && value instanceof Array && value.length > 0) {
10 | const permissions = useUserStore().permissions
11 | const permissionDatas = value
12 | const all_permission = "*:*:*";
13 |
14 | const hasPermission = permissions.some(permission => {
15 | return all_permission === permission || permissionDatas.includes(permission)
16 | })
17 |
18 | if (!hasPermission) {
19 | return false
20 | }
21 | return true
22 | } else {
23 | console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
24 | return false
25 | }
26 | }
27 |
28 | /**
29 | * 角色权限校验
30 | * @param {Array} value 校验值
31 | * @returns {Boolean}
32 | */
33 | export function checkRole(value) {
34 | if (value && value instanceof Array && value.length > 0) {
35 | const roles = useUserStore().roles
36 | const permissionRoles = value
37 | const super_admin = "admin";
38 |
39 | const hasRole = roles.some(role => {
40 | return super_admin === role || permissionRoles.includes(role)
41 | })
42 |
43 | if (!hasRole) {
44 | return false
45 | }
46 | return true
47 | } else {
48 | console.error(`need roles! Like checkRole="['admin','editor']"`)
49 | return false
50 | }
51 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/.env.dockermy:
--------------------------------------------------------------------------------
1 | # -------- 应用配置 --------
2 | # 应用运行环境
3 | APP_ENV = 'prod'
4 | # 应用名称
5 | APP_NAME = 'RuoYi-FastAPI'
6 | # 应用代理路径
7 | APP_ROOT_PATH = '/docker-api'
8 | # 应用主机
9 | APP_HOST = '0.0.0.0'
10 | # 应用端口
11 | APP_PORT = 9099
12 | # 应用版本
13 | APP_VERSION= '1.8.0'
14 | # 应用是否开启热重载
15 | APP_RELOAD = false
16 | # 应用是否开启IP归属区域查询
17 | APP_IP_LOCATION_QUERY = true
18 | # 应用是否允许账号同时登录
19 | APP_SAME_TIME_LOGIN = true
20 |
21 | # -------- Jwt配置 --------
22 | # Jwt秘钥
23 | JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
24 | # Jwt算法
25 | JWT_ALGORITHM = 'HS256'
26 | # 令牌过期时间
27 | JWT_EXPIRE_MINUTES = 1440
28 | # redis中令牌过期时间
29 | JWT_REDIS_EXPIRE_MINUTES = 30
30 |
31 |
32 | # -------- 数据库配置 --------
33 | # 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
34 | DB_TYPE = 'mysql'
35 | # 数据库主机
36 | DB_HOST = 'ruoyi-mysql'
37 | # 数据库端口
38 | DB_PORT = 3306
39 | # 数据库用户名
40 | DB_USERNAME = 'root'
41 | # 数据库密码
42 | DB_PASSWORD = 'root'
43 | # 数据库名称
44 | DB_DATABASE = 'ruoyi-fastapi'
45 | # 是否开启sqlalchemy日志
46 | DB_ECHO = true
47 | # 允许溢出连接池大小的最大连接数
48 | DB_MAX_OVERFLOW = 10
49 | # 连接池大小,0表示连接数无限制
50 | DB_POOL_SIZE = 50
51 | # 连接回收时间(单位:秒)
52 | DB_POOL_RECYCLE = 3600
53 | # 连接池中没有线程可用时,最多等待的时间(单位:秒)
54 | DB_POOL_TIMEOUT = 30
55 |
56 | # -------- Redis配置 --------
57 | # Redis主机
58 | REDIS_HOST = 'ruoyi-redis'
59 | # Redis端口
60 | REDIS_PORT = 6379
61 | # Redis用户名
62 | REDIS_USERNAME = ''
63 | # Redis密码
64 | REDIS_PASSWORD = ''
65 | # Redis数据库
66 | REDIS_DATABASE = 2
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/.env.dockerpg:
--------------------------------------------------------------------------------
1 | # -------- 应用配置 --------
2 | # 应用运行环境
3 | APP_ENV = 'prod'
4 | # 应用名称
5 | APP_NAME = 'RuoYi-FastAPI'
6 | # 应用代理路径
7 | APP_ROOT_PATH = '/docker-api'
8 | # 应用主机
9 | APP_HOST = '0.0.0.0'
10 | # 应用端口
11 | APP_PORT = 9099
12 | # 应用版本
13 | APP_VERSION= '1.8.0'
14 | # 应用是否开启热重载
15 | APP_RELOAD = false
16 | # 应用是否开启IP归属区域查询
17 | APP_IP_LOCATION_QUERY = true
18 | # 应用是否允许账号同时登录
19 | APP_SAME_TIME_LOGIN = true
20 |
21 | # -------- Jwt配置 --------
22 | # Jwt秘钥
23 | JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
24 | # Jwt算法
25 | JWT_ALGORITHM = 'HS256'
26 | # 令牌过期时间
27 | JWT_EXPIRE_MINUTES = 1440
28 | # redis中令牌过期时间
29 | JWT_REDIS_EXPIRE_MINUTES = 30
30 |
31 |
32 | # -------- 数据库配置 --------
33 | # 数据库类型,可选的有'mysql'、'postgresql',默认为'mysql'
34 | DB_TYPE = 'postgresql'
35 | # 数据库主机
36 | DB_HOST = 'ruoyi-pg'
37 | # 数据库端口
38 | DB_PORT = 5432
39 | # 数据库用户名
40 | DB_USERNAME = 'postgres'
41 | # 数据库密码
42 | DB_PASSWORD = 'root'
43 | # 数据库名称
44 | DB_DATABASE = 'ruoyi-fastapi'
45 | # 是否开启sqlalchemy日志
46 | DB_ECHO = true
47 | # 允许溢出连接池大小的最大连接数
48 | DB_MAX_OVERFLOW = 10
49 | # 连接池大小,0表示连接数无限制
50 | DB_POOL_SIZE = 50
51 | # 连接回收时间(单位:秒)
52 | DB_POOL_RECYCLE = 3600
53 | # 连接池中没有线程可用时,最多等待的时间(单位:秒)
54 | DB_POOL_TIMEOUT = 30
55 |
56 | # -------- Redis配置 --------
57 | # Redis主机
58 | REDIS_HOST = 'ruoyi-redis'
59 | # Redis端口
60 | REDIS_PORT = 6379
61 | # Redis用户名
62 | REDIS_USERNAME = ''
63 | # Redis密码
64 | REDIS_PASSWORD = ''
65 | # Redis数据库
66 | REDIS_DATABASE = 2
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/do/config_do.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from sqlalchemy import CHAR, Column, DateTime, Integer, String
4 |
5 | from config.database import Base
6 | from config.env import DataBaseConfig
7 | from utils.common_util import SqlalchemyUtil
8 |
9 |
10 | class SysConfig(Base):
11 | """
12 | 参数配置表
13 | """
14 |
15 | __tablename__ = 'sys_config'
16 | __table_args__ = {'comment': '参数配置表'}
17 |
18 | config_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment='参数主键')
19 | config_name = Column(String(100), nullable=True, server_default="''", comment='参数名称')
20 | config_key = Column(String(100), nullable=True, server_default="''", comment='参数键名')
21 | config_value = Column(String(500), nullable=True, server_default="''", comment='参数键值')
22 | config_type = Column(CHAR(1), nullable=True, server_default='N', comment='系统内置(Y是 N否)')
23 | create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
24 | create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
25 | update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
26 | update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
27 | remark = Column(
28 | String(500),
29 | nullable=True,
30 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
31 | comment='备注',
32 | )
33 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/theme.js:
--------------------------------------------------------------------------------
1 | // 处理主题样式
2 | export function handleThemeStyle(theme) {
3 | document.documentElement.style.setProperty('--el-color-primary', theme)
4 | for (let i = 1; i <= 9; i++) {
5 | document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, `${getLightColor(theme, i / 10)}`)
6 | }
7 | for (let i = 1; i <= 9; i++) {
8 | document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`)
9 | }
10 | }
11 |
12 | // hex颜色转rgb颜色
13 | export function hexToRgb(str) {
14 | str = str.replace('#', '')
15 | let hexs = str.match(/../g)
16 | for (let i = 0; i < 3; i++) {
17 | hexs[i] = parseInt(hexs[i], 16)
18 | }
19 | return hexs
20 | }
21 |
22 | // rgb颜色转Hex颜色
23 | export function rgbToHex(r, g, b) {
24 | let hexs = [r.toString(16), g.toString(16), b.toString(16)]
25 | for (let i = 0; i < 3; i++) {
26 | if (hexs[i].length == 1) {
27 | hexs[i] = `0${hexs[i]}`
28 | }
29 | }
30 | return `#${hexs.join('')}`
31 | }
32 |
33 | // 变浅颜色值
34 | export function getLightColor(color, level) {
35 | let rgb = hexToRgb(color)
36 | for (let i = 0; i < 3; i++) {
37 | rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
38 | }
39 | return rgbToHex(rgb[0], rgb[1], rgb[2])
40 | }
41 |
42 | // 变深颜色值
43 | export function getDarkColor(color, level) {
44 | let rgb = hexToRgb(color)
45 | for (let i = 0; i < 3; i++) {
46 | rgb[i] = Math.floor(rgb[i] * (1 - level))
47 | }
48 | return rgbToHex(rgb[0], rgb[1], rgb[2])
49 | }
50 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/phone.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/log.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/plugins/auth.js:
--------------------------------------------------------------------------------
1 | import useUserStore from '@/store/modules/user'
2 |
3 | function authPermission(permission) {
4 | const all_permission = "*:*:*";
5 | const permissions = useUserStore().permissions
6 | if (permission && permission.length > 0) {
7 | return permissions.some(v => {
8 | return all_permission === v || v === permission
9 | })
10 | } else {
11 | return false
12 | }
13 | }
14 |
15 | function authRole(role) {
16 | const super_admin = "admin";
17 | const roles = useUserStore().roles
18 | if (role && role.length > 0) {
19 | return roles.some(v => {
20 | return super_admin === v || v === role
21 | })
22 | } else {
23 | return false
24 | }
25 | }
26 |
27 | export default {
28 | // 验证用户是否具备某权限
29 | hasPermi(permission) {
30 | return authPermission(permission);
31 | },
32 | // 验证用户是否含有指定权限,只需包含其中一个
33 | hasPermiOr(permissions) {
34 | return permissions.some(item => {
35 | return authPermission(item)
36 | })
37 | },
38 | // 验证用户是否含有指定权限,必须全部拥有
39 | hasPermiAnd(permissions) {
40 | return permissions.every(item => {
41 | return authPermission(item)
42 | })
43 | },
44 | // 验证用户是否具备某角色
45 | hasRole(role) {
46 | return authRole(role);
47 | },
48 | // 验证用户是否含有指定角色,只需包含其中一个
49 | hasRoleOr(roles) {
50 | return roles.some(item => {
51 | return authRole(item)
52 | })
53 | },
54 | // 验证用户是否含有指定角色,必须全部拥有
55 | hasRoleAnd(roles) {
56 | return roles.every(item => {
57 | return authRole(item)
58 | })
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/bug.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/service/captcha_service.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import io
3 | import os
4 | import random
5 |
6 | from PIL import Image, ImageDraw, ImageFont
7 |
8 |
9 | class CaptchaService:
10 | """
11 | 验证码模块服务层
12 | """
13 |
14 | @classmethod
15 | async def create_captcha_image_service(cls) -> list[str, int]:
16 | # 创建空白图像
17 | image = Image.new('RGB', (160, 60), color='#EAEAEA')
18 |
19 | # 创建绘图对象
20 | draw = ImageDraw.Draw(image)
21 |
22 | # 设置字体
23 | font = ImageFont.truetype(os.path.join(os.path.abspath(os.getcwd()), 'assets', 'font', 'Arial.ttf'), size=30)
24 |
25 | # 生成两个0-9之间的随机整数
26 | num1 = random.randint(0, 9)
27 | num2 = random.randint(0, 9)
28 | # 从运算符列表中随机选择一个
29 | operational_character_list = ['+', '-', '*']
30 | operational_character = random.choice(operational_character_list)
31 | # 根据选择的运算符进行计算
32 | if operational_character == '+':
33 | result = num1 + num2
34 | elif operational_character == '-':
35 | result = num1 - num2
36 | else:
37 | result = num1 * num2
38 | # 绘制文本
39 | text = f'{num1} {operational_character} {num2} = ?'
40 | draw.text((25, 15), text, fill='blue', font=font)
41 |
42 | # 将图像数据保存到内存中
43 | buffer = io.BytesIO()
44 | image.save(buffer, format='PNG')
45 |
46 | # 将图像数据转换为base64字符串
47 | base64_string = base64.b64encode(buffer.getvalue()).decode()
48 |
49 | return [base64_string, result]
50 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix {
2 | &:after {
3 | content: "";
4 | display: table;
5 | clear: both;
6 | }
7 | }
8 |
9 | @mixin scrollBar {
10 | &::-webkit-scrollbar-track-piece {
11 | background: #d3dce6;
12 | }
13 |
14 | &::-webkit-scrollbar {
15 | width: 6px;
16 | }
17 |
18 | &::-webkit-scrollbar-thumb {
19 | background: #99a9bf;
20 | border-radius: 20px;
21 | }
22 | }
23 |
24 | @mixin relative {
25 | position: relative;
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
30 | @mixin pct($pct) {
31 | width: #{$pct};
32 | position: relative;
33 | margin: 0 auto;
34 | }
35 |
36 | @mixin triangle($width, $height, $color, $direction) {
37 | $width: $width/2;
38 | $color-border-style: $height solid $color;
39 | $transparent-border-style: $width solid transparent;
40 | height: 0;
41 | width: 0;
42 |
43 | @if $direction==up {
44 | border-bottom: $color-border-style;
45 | border-left: $transparent-border-style;
46 | border-right: $transparent-border-style;
47 | }
48 |
49 | @else if $direction==right {
50 | border-left: $color-border-style;
51 | border-top: $transparent-border-style;
52 | border-bottom: $transparent-border-style;
53 | }
54 |
55 | @else if $direction==down {
56 | border-top: $color-border-style;
57 | border-left: $transparent-border-style;
58 | border-right: $transparent-border-style;
59 | }
60 |
61 | @else if $direction==left {
62 | border-right: $color-border-style;
63 | border-top: $transparent-border-style;
64 | border-bottom: $transparent-border-style;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/middlewares/trace_middleware/span.py:
--------------------------------------------------------------------------------
1 | from collections.abc import AsyncGenerator
2 | from contextlib import asynccontextmanager
3 |
4 | from starlette.types import Message, Scope
5 |
6 | from .ctx import TraceCtx
7 |
8 |
9 | class Span:
10 | """
11 | 整个http生命周期:
12 | request(before) --> request(after) --> response(before) --> response(after)
13 | """
14 |
15 | def __init__(self, scope: Scope) -> None:
16 | self.scope = scope
17 |
18 | async def request_before(self) -> None:
19 | """
20 | request_before: 处理header信息等, 如记录请求体信息
21 | """
22 | TraceCtx.set_id()
23 |
24 | async def request_after(self, message: Message) -> Message:
25 | """
26 | request_after: 处理请求bytes, 如记录请求参数
27 |
28 | example:
29 | message: {'type': 'http.request', 'body': b'{\r\n "name": "\xe8\x8b\x8f\xe8\x8b\x8f\xe8\x8b\x8f"\r\n}', 'more_body': False}
30 | """
31 | return message
32 |
33 | async def response(self, message: Message) -> Message:
34 | """
35 | if message['type'] == "http.response.start": -----> request-before
36 | pass
37 | if message['type'] == "http.response.body": -----> request-after
38 | message.get('body', b'')
39 | pass
40 | """
41 | if message['type'] == 'http.response.start':
42 | message['headers'].append((b'request-id', TraceCtx.get_id().encode()))
43 | return message
44 |
45 |
46 | @asynccontextmanager
47 | async def get_current_span(scope: Scope) -> AsyncGenerator[Span, None]:
48 | yield Span(scope)
49 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/utils/dependency_util.py:
--------------------------------------------------------------------------------
1 | from fastapi import Request
2 |
3 | from common.context import RequestContext
4 | from config.env import AppConfig
5 | from exceptions.exception import PermissionException
6 |
7 |
8 | class DependencyUtil:
9 | """
10 | 依赖项工具类
11 | """
12 |
13 | @classmethod
14 | def check_exclude_routes(cls, request: Request, err_msg: str = '当前路由不在认证规则内,不可使用该依赖项') -> None:
15 | """
16 | 检查路径和方法是否匹配排除路由模式
17 |
18 | :param request: 请求对象
19 | :param err_msg: 错误信息
20 | :return: None
21 | """
22 | # 获取当前请求路径和方法
23 | path = request.url.path
24 | method = request.method.upper()
25 |
26 | # 从配置中获取APP_ROOT_PATH
27 | app_root_path = AppConfig.app_root_path
28 |
29 | # 去掉APP_ROOT_PATH前缀
30 | if app_root_path and path.startswith(app_root_path):
31 | path = path[len(app_root_path) :]
32 |
33 | # 获取编译后的排除路由模式列表
34 | exclude_patterns = RequestContext.get_current_exclude_patterns()
35 |
36 | # 检查当前路由是否在排除路由列表中
37 | if path and method and exclude_patterns:
38 | for item in exclude_patterns:
39 | pattern = item['pattern']
40 | exclude_methods = item['methods']
41 | ignore_paths = item['ignore_paths']
42 |
43 | # 检查当前路径是否在忽略列表中
44 | if path in ignore_paths:
45 | continue
46 |
47 | # 检查路径是否匹配,并且methods为空列表(匹配所有方法)或者当前方法在允许列表中
48 | if pattern.match(path) and (not exclude_methods or method in exclude_methods):
49 | raise PermissionException(data='', message=err_msg)
50 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vfadmin",
3 | "version": "1.8.0",
4 | "description": "vfadmin管理系统",
5 | "author": "insistence",
6 | "license": "MIT",
7 | "type": "module",
8 | "scripts": {
9 | "dev": "vite",
10 | "build:prod": "vite build",
11 | "build:docker": "vite build --mode docker",
12 | "build:stage": "vite build --mode staging",
13 | "preview": "vite preview"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git"
18 | },
19 | "dependencies": {
20 | "@ant-design/icons-vue": "^7.0.1",
21 | "@antv/g2plot": "^2.4.31",
22 | "@element-plus/icons-vue": "2.3.1",
23 | "@vueup/vue-quill": "1.2.0",
24 | "@vueuse/core": "13.3.0",
25 | "ant-design-vue": "^4.1.1",
26 | "axios": "1.9.0",
27 | "clipboard": "2.0.11",
28 | "echarts": "5.6.0",
29 | "element-plus": "2.10.7",
30 | "file-saver": "2.0.5",
31 | "fuse.js": "6.6.2",
32 | "js-beautify": "1.15.1",
33 | "js-cookie": "3.0.5",
34 | "jsencrypt": "3.3.2",
35 | "nprogress": "0.2.0",
36 | "pinia": "3.0.2",
37 | "splitpanes": "4.0.4",
38 | "vue": "3.5.16",
39 | "vue-cropper": "1.1.1",
40 | "vue-router": "4.5.1",
41 | "vuedraggable": "4.1.0"
42 | },
43 | "devDependencies": {
44 | "@vitejs/plugin-vue": "5.2.4",
45 | "less": "^4.2.0",
46 | "sass-embedded": "1.89.1",
47 | "unplugin-auto-import": "0.18.6",
48 | "unplugin-vue-setup-extend-plus": "1.0.1",
49 | "vite": "6.3.5",
50 | "vite-plugin-compression": "0.5.1",
51 | "vite-plugin-svg-icons": "2.0.1"
52 | },
53 | "overrides": {
54 | "quill": "2.0.2"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/exceptions/exception.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 |
4 | class LoginException(Exception):
5 | """
6 | 自定义登录异常LoginException
7 | """
8 |
9 | def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
10 | self.data = data
11 | self.message = message
12 |
13 |
14 | class AuthException(Exception):
15 | """
16 | 自定义令牌异常AuthException
17 | """
18 |
19 | def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
20 | self.data = data
21 | self.message = message
22 |
23 |
24 | class PermissionException(Exception):
25 | """
26 | 自定义权限异常PermissionException
27 | """
28 |
29 | def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
30 | self.data = data
31 | self.message = message
32 |
33 |
34 | class ServiceException(Exception):
35 | """
36 | 自定义服务异常ServiceException
37 | """
38 |
39 | def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
40 | self.data = data
41 | self.message = message
42 |
43 |
44 | class ServiceWarning(Exception):
45 | """
46 | 自定义服务警告ServiceWarning
47 | """
48 |
49 | def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
50 | self.data = data
51 | self.message = message
52 |
53 |
54 | class ModelValidatorException(Exception):
55 | """
56 | 自定义模型校验异常ModelValidatorException
57 | """
58 |
59 | def __init__(self, data: Optional[str] = None, message: Optional[str] = None) -> None:
60 | self.data = data
61 | self.message = message
62 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/do/notice_do.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from sqlalchemy import CHAR, Column, DateTime, Integer, LargeBinary, String
4 | from sqlalchemy.dialects import mysql
5 |
6 | from config.database import Base
7 | from config.env import DataBaseConfig
8 | from utils.common_util import SqlalchemyUtil
9 |
10 |
11 | class SysNotice(Base):
12 | """
13 | 通知公告表
14 | """
15 |
16 | __tablename__ = 'sys_notice'
17 | __table_args__ = {'comment': '通知公告表'}
18 |
19 | notice_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True, comment='公告ID')
20 | notice_title = Column(String(50), nullable=False, comment='公告标题')
21 | notice_type = Column(CHAR(1), nullable=False, comment='公告类型(1通知 2公告)')
22 | notice_content = Column(
23 | mysql.LONGBLOB if DataBaseConfig.db_type == 'mysql' else LargeBinary,
24 | nullable=True,
25 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type, False),
26 | comment='公告内容',
27 | )
28 | status = Column(CHAR(1), nullable=True, server_default='0', comment='公告状态(0正常 1关闭)')
29 | create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
30 | create_time = Column(DateTime, nullable=True, comment='创建时间', default=datetime.now())
31 | update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
32 | update_time = Column(DateTime, nullable=True, comment='更新时间', default=datetime.now())
33 | remark = Column(
34 | String(255),
35 | nullable=True,
36 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
37 | comment='备注',
38 | )
39 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/pdf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/logininfor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/rate.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/vo/online_vo.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from typing import Optional
3 |
4 | from pydantic import BaseModel, ConfigDict, Field
5 | from pydantic.alias_generators import to_camel
6 |
7 |
8 | class OnlineModel(BaseModel):
9 | """
10 | 在线用户对应pydantic模型
11 | """
12 |
13 | model_config = ConfigDict(alias_generator=to_camel)
14 |
15 | token_id: Optional[str] = Field(default=None, description='会话编号')
16 | user_name: Optional[str] = Field(default=None, description='登录名称')
17 | dept_name: Optional[str] = Field(default=None, description='所属部门')
18 | ipaddr: Optional[str] = Field(default=None, description='主机')
19 | login_location: Optional[str] = Field(default=None, description='登录地点')
20 | browser: Optional[str] = Field(default=None, description='浏览器类型')
21 | os: Optional[str] = Field(default=None, description='操作系统')
22 | login_time: Optional[datetime] = Field(default=None, description='登录时间')
23 |
24 |
25 | class OnlineQueryModel(OnlineModel):
26 | """
27 | 岗位管理不分页查询模型
28 | """
29 |
30 | begin_time: Optional[str] = Field(default=None, description='开始时间')
31 | end_time: Optional[str] = Field(default=None, description='结束时间')
32 |
33 |
34 | class OnlinePageResponseModel(BaseModel):
35 | """
36 | 在线用户分页响应模型
37 | """
38 |
39 | model_config = ConfigDict(alias_generator=to_camel)
40 |
41 | rows: list[OnlineModel] = Field(description='在线用户记录列表')
42 | total: int = Field(description='总记录数')
43 |
44 |
45 | class DeleteOnlineModel(BaseModel):
46 | """
47 | 强退在线用户模型
48 | """
49 |
50 | model_config = ConfigDict(alias_generator=to_camel)
51 |
52 | token_ids: str = Field(description='需要强退的会话编号')
53 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/job.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/plugins/cache.js:
--------------------------------------------------------------------------------
1 | const sessionCache = {
2 | set (key, value) {
3 | if (!sessionStorage) {
4 | return
5 | }
6 | if (key != null && value != null) {
7 | sessionStorage.setItem(key, value)
8 | }
9 | },
10 | get (key) {
11 | if (!sessionStorage) {
12 | return null
13 | }
14 | if (key == null) {
15 | return null
16 | }
17 | return sessionStorage.getItem(key)
18 | },
19 | setJSON (key, jsonValue) {
20 | if (jsonValue != null) {
21 | this.set(key, JSON.stringify(jsonValue))
22 | }
23 | },
24 | getJSON (key) {
25 | const value = this.get(key)
26 | if (value != null) {
27 | return JSON.parse(value)
28 | }
29 | return null
30 | },
31 | remove (key) {
32 | sessionStorage.removeItem(key);
33 | }
34 | }
35 | const localCache = {
36 | set (key, value) {
37 | if (!localStorage) {
38 | return
39 | }
40 | if (key != null && value != null) {
41 | localStorage.setItem(key, value)
42 | }
43 | },
44 | get (key) {
45 | if (!localStorage) {
46 | return null
47 | }
48 | if (key == null) {
49 | return null
50 | }
51 | return localStorage.getItem(key)
52 | },
53 | setJSON (key, jsonValue) {
54 | if (jsonValue != null) {
55 | this.set(key, JSON.stringify(jsonValue))
56 | }
57 | },
58 | getJSON (key) {
59 | const value = this.get(key)
60 | if (value != null) {
61 | return JSON.parse(value)
62 | }
63 | return null
64 | },
65 | remove (key) {
66 | localStorage.removeItem(key);
67 | }
68 | }
69 |
70 | export default {
71 | /**
72 | * 会话级缓存
73 | */
74 | session: sessionCache,
75 | /**
76 | * 本地缓存
77 | */
78 | local: localCache
79 | }
80 |
--------------------------------------------------------------------------------
/docker-compose.pg.yml:
--------------------------------------------------------------------------------
1 | services:
2 | # 前端服务
3 | ruoyi-frontend:
4 | build:
5 | context: ./ruoyi-fastapi-frontend
6 | dockerfile: Dockerfile
7 | image: ruoyi-frontend:latest
8 | container_name: ruoyi-frontend
9 | ports:
10 | - "12580:80"
11 | volumes:
12 | - ./ruoyi-fastapi-frontend/bin/nginx.dockerpg.conf:/etc/nginx/conf.d/default.conf
13 | depends_on:
14 | - ruoyi-backend-pg
15 | networks:
16 | - ruoyi-network
17 |
18 | # 后端服务(PostgreSQL版本)
19 | ruoyi-backend-pg:
20 | build:
21 | context: ./ruoyi-fastapi-backend
22 | dockerfile: Dockerfile.pg
23 | image: ruoyi-backend-pg:latest
24 | container_name: ruoyi-backend-pg
25 | ports:
26 | - "19099:9099"
27 | depends_on:
28 | - ruoyi-pg
29 | - ruoyi-redis
30 | networks:
31 | - ruoyi-network
32 |
33 | # PostgreSQL服务
34 | ruoyi-pg:
35 | image: postgres:14
36 | container_name: ruoyi-pg
37 | environment:
38 | POSTGRES_PASSWORD: root
39 | POSTGRES_DB: ruoyi-fastapi
40 | POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate=C --lc-ctype=C
41 | ports:
42 | - "15432:5432"
43 | volumes:
44 | - ./ruoyi-fastapi-backend/sql/ruoyi-fastapi-pg.sql:/docker-entrypoint-initdb.d/ruoyi-fastapi-pg.sql
45 | networks:
46 | - ruoyi-network
47 | healthcheck:
48 | test: ["CMD", "pg_isready", "-U", "postgres"]
49 | interval: 5s
50 | timeout: 10s
51 | retries: 30
52 |
53 | # Redis服务
54 | ruoyi-redis:
55 | image: redis:latest
56 | container_name: ruoyi-redis
57 | ports:
58 | - "16379:6379"
59 | networks:
60 | - ruoyi-network
61 |
62 | # 网络配置
63 | networks:
64 | ruoyi-network:
65 | name: ruoyi-network
66 | driver: bridge
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py:
--------------------------------------------------------------------------------
1 | import uuid
2 | from datetime import timedelta
3 |
4 | from fastapi import Request, Response
5 |
6 | from common.enums import RedisInitKeyConfig
7 | from common.router import APIRouterPro
8 | from common.vo import DynamicResponseModel
9 | from module_admin.entity.vo.login_vo import CaptchaCode
10 | from module_admin.service.captcha_service import CaptchaService
11 | from utils.log_util import logger
12 | from utils.response_util import ResponseUtil
13 |
14 | captcha_controller = APIRouterPro(order_num=2, tags=['验证码模块'])
15 |
16 |
17 | @captcha_controller.get(
18 | '/captchaImage',
19 | summary='获取图片验证码接口',
20 | description='用于获取图片验证码',
21 | response_model=DynamicResponseModel[CaptchaCode],
22 | )
23 | async def get_captcha_image(request: Request) -> Response:
24 | captcha_enabled = (
25 | await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.captchaEnabled') == 'true'
26 | )
27 | register_enabled = (
28 | await request.app.state.redis.get(f'{RedisInitKeyConfig.SYS_CONFIG.key}:sys.account.registerUser') == 'true'
29 | )
30 | session_id = str(uuid.uuid4())
31 | captcha_result = await CaptchaService.create_captcha_image_service()
32 | image = captcha_result[0]
33 | computed_result = captcha_result[1]
34 | await request.app.state.redis.set(
35 | f'{RedisInitKeyConfig.CAPTCHA_CODES.key}:{session_id}', computed_result, ex=timedelta(minutes=2)
36 | )
37 | logger.info(f'编号为{session_id}的会话获取图片验证码成功')
38 |
39 | return ResponseUtil.success(
40 | model_content=CaptchaCode(
41 | captchaEnabled=captcha_enabled, registerEnabled=register_enabled, img=image, uuid=session_id
42 | )
43 | )
44 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | // cover some element-ui styles
2 |
3 | .el-breadcrumb__inner,
4 | .el-breadcrumb__inner a {
5 | font-weight: 400 !important;
6 | }
7 |
8 | .el-upload {
9 | input[type="file"] {
10 | display: none !important;
11 | }
12 | }
13 |
14 | .el-upload__input {
15 | display: none;
16 | }
17 |
18 | .cell {
19 | .el-tag {
20 | margin-right: 0px;
21 | }
22 | }
23 |
24 | .small-padding {
25 | .cell {
26 | padding-left: 5px;
27 | padding-right: 5px;
28 | }
29 | }
30 |
31 | .fixed-width {
32 | .el-button--mini {
33 | padding: 7px 10px;
34 | width: 60px;
35 | }
36 | }
37 |
38 | .status-col {
39 | .cell {
40 | padding: 0 10px;
41 | text-align: center;
42 |
43 | .el-tag {
44 | margin-right: 0px;
45 | }
46 | }
47 | }
48 |
49 | // to fixed https://github.com/ElemeFE/element/issues/2461
50 | .el-dialog {
51 | transform: none;
52 | left: 0;
53 | position: relative;
54 | margin: 0 auto;
55 | }
56 |
57 | // refine element ui upload
58 | .upload-container {
59 | .el-upload {
60 | width: 100%;
61 |
62 | .el-upload-dragger {
63 | width: 100%;
64 | height: 200px;
65 | }
66 | }
67 | }
68 |
69 | // dropdown
70 | .el-dropdown-menu {
71 | a {
72 | display: block
73 | }
74 | }
75 |
76 | // fix date-picker ui bug in filter-item
77 | .el-range-editor.el-input__inner {
78 | display: inline-flex !important;
79 | }
80 |
81 | // to fix el-date-picker css style
82 | .el-range-separator {
83 | box-sizing: content-box;
84 | }
85 |
86 | .el-menu--collapse
87 | > div
88 | > .el-submenu
89 | > .el-submenu__title
90 | .el-submenu__icon-arrow {
91 | display: none;
92 | }
93 |
94 | .el-dropdown .el-dropdown-link{
95 | color: var(--el-color-primary) !important;
96 | }
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/tool/gen/basicInfoForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
49 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/exit-fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/tree.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/swagger.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_generator/templates/python/do.py.jinja2:
--------------------------------------------------------------------------------
1 | {% for do_import in doImportList %}
2 | {{ do_import }}
3 | {% endfor %}
4 | {% if table.sub %}
5 | from sqlalchemy.orm import relationship
6 | {% endif %}
7 |
8 | from config.database import Base
9 |
10 |
11 | class {{ ClassName }}(Base):
12 | """
13 | {{ functionName }}表
14 | """
15 |
16 | __tablename__ = '{{ tableName }}'
17 | __table_args__ = {'comment': '{{ tableComment }}'}
18 |
19 | {% for column in columns %}
20 | {{ column.column_name }} = Column({{ column.column_type | get_sqlalchemy_type }}, {% if column.pk %}primary_key=True, {% endif %}{% if column.increment %}autoincrement=True, {% endif %}{% if column.required or column.pk %}nullable=False{% else %}nullable=True{% endif %}, comment='{{ column.column_comment }}')
21 | {% endfor %}
22 |
23 | {% if table.sub %}
24 | {{ subclassName }}_list = relationship('{{ subClassName }}', back_populates='{{ businessName }}')
25 | {% endif %}
26 |
27 |
28 | {% if table.sub %}
29 | class {{ subClassName }}(Base):
30 | """
31 | {{ subTable.function_name }}表
32 | """
33 |
34 | __tablename__ = '{{ subTableName }}'
35 |
36 | {% for column in subTable.columns %}
37 | {{ column.column_name }} = Column({{ column.column_type | get_sqlalchemy_type }}, {% if column.column_name == subTableFkName %}ForeignKey('{{ tableName }}.{{ subTableFkName }}'), {% endif %}{% if column.pk %}primary_key=True, {% endif %}{% if column.increment %}autoincrement=True, {% endif %}{% if column.required %}nullable=True{% else %}nullable=False{% endif %}, comment='{{ column.column_comment }}')
38 | {% endfor %}
39 |
40 | {% if table.sub %}
41 | {{ businessName }} = relationship('{{ ClassName }}', back_populates='{{ subclassName }}_list')
42 | {% endif %}
43 | {% endif %}
44 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/password.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/api/tool/gen.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | // 查询生成表数据
4 | export function listTable(query) {
5 | return request({
6 | url: '/tool/gen/list',
7 | method: 'get',
8 | params: query
9 | })
10 | }
11 | // 查询db数据库列表
12 | export function listDbTable(query) {
13 | return request({
14 | url: '/tool/gen/db/list',
15 | method: 'get',
16 | params: query
17 | })
18 | }
19 |
20 | // 查询表详细信息
21 | export function getGenTable(tableId) {
22 | return request({
23 | url: '/tool/gen/' + tableId,
24 | method: 'get'
25 | })
26 | }
27 |
28 | // 修改代码生成信息
29 | export function updateGenTable(data) {
30 | return request({
31 | url: '/tool/gen',
32 | method: 'put',
33 | data: data
34 | })
35 | }
36 |
37 | // 导入表
38 | export function importTable(data) {
39 | return request({
40 | url: '/tool/gen/importTable',
41 | method: 'post',
42 | params: data
43 | })
44 | }
45 |
46 | // 创建表
47 | export function createTable(data) {
48 | return request({
49 | url: '/tool/gen/createTable',
50 | method: 'post',
51 | params: data
52 | })
53 | }
54 |
55 | // 预览生成代码
56 | export function previewTable(tableId) {
57 | return request({
58 | url: '/tool/gen/preview/' + tableId,
59 | method: 'get'
60 | })
61 | }
62 |
63 | // 删除表数据
64 | export function delTable(tableId) {
65 | return request({
66 | url: '/tool/gen/' + tableId,
67 | method: 'delete'
68 | })
69 | }
70 |
71 | // 生成代码(自定义路径)
72 | export function genCode(tableName) {
73 | return request({
74 | url: '/tool/gen/genCode/' + tableName,
75 | method: 'get'
76 | })
77 | }
78 |
79 | // 同步数据库
80 | export function synchDb(tableName) {
81 | return request({
82 | url: '/tool/gen/synchDb/' + tableName,
83 | method: 'get'
84 | })
85 | }
86 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/directive/common/copyText.js:
--------------------------------------------------------------------------------
1 | /**
2 | * v-copyText 复制文本内容
3 | * Copyright (c) 2022 ruoyi
4 | */
5 | export default {
6 | beforeMount(el, { value, arg }) {
7 | if (arg === "callback") {
8 | el.$copyCallback = value;
9 | } else {
10 | el.$copyValue = value;
11 | const handler = () => {
12 | copyTextToClipboard(el.$copyValue);
13 | if (el.$copyCallback) {
14 | el.$copyCallback(el.$copyValue);
15 | }
16 | };
17 | el.addEventListener("click", handler);
18 | el.$destroyCopy = () => el.removeEventListener("click", handler);
19 | }
20 | }
21 | }
22 |
23 | function copyTextToClipboard(input, { target = document.body } = {}) {
24 | const element = document.createElement('textarea');
25 | const previouslyFocusedElement = document.activeElement;
26 |
27 | element.value = input;
28 |
29 | // Prevent keyboard from showing on mobile
30 | element.setAttribute('readonly', '');
31 |
32 | element.style.contain = 'strict';
33 | element.style.position = 'absolute';
34 | element.style.left = '-9999px';
35 | element.style.fontSize = '12pt'; // Prevent zooming on iOS
36 |
37 | const selection = document.getSelection();
38 | const originalRange = selection.rangeCount > 0 && selection.getRangeAt(0);
39 |
40 | target.append(element);
41 | element.select();
42 |
43 | // Explicit selection workaround for iOS
44 | element.selectionStart = 0;
45 | element.selectionEnd = input.length;
46 |
47 | let isSuccess = false;
48 | try {
49 | isSuccess = document.execCommand('copy');
50 | } catch { }
51 |
52 | element.remove();
53 |
54 | if (originalRange) {
55 | selection.removeAllRanges();
56 | selection.addRange(originalRange);
57 | }
58 |
59 | // Get the focus back on the previously focused element, if any
60 | if (previouslyFocusedElement) {
61 | previouslyFocusedElement.focus();
62 | }
63 |
64 | return isSuccess;
65 | }
66 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/styles/btn.scss:
--------------------------------------------------------------------------------
1 | @use './variables.module.scss' as *;
2 |
3 | @mixin colorBtn($color) {
4 | background: $color;
5 |
6 | &:hover {
7 | color: $color;
8 |
9 | &:before,
10 | &:after {
11 | background: $color;
12 | }
13 | }
14 | }
15 |
16 | .blue-btn {
17 | @include colorBtn($blue)
18 | }
19 |
20 | .light-blue-btn {
21 | @include colorBtn($light-blue)
22 | }
23 |
24 | .red-btn {
25 | @include colorBtn($red)
26 | }
27 |
28 | .pink-btn {
29 | @include colorBtn($pink)
30 | }
31 |
32 | .green-btn {
33 | @include colorBtn($green)
34 | }
35 |
36 | .tiffany-btn {
37 | @include colorBtn($tiffany)
38 | }
39 |
40 | .yellow-btn {
41 | @include colorBtn($yellow)
42 | }
43 |
44 | .pan-btn {
45 | font-size: 14px;
46 | color: #fff;
47 | padding: 14px 36px;
48 | border-radius: 8px;
49 | border: none;
50 | outline: none;
51 | transition: 600ms ease all;
52 | position: relative;
53 | display: inline-block;
54 |
55 | &:hover {
56 | background: #fff;
57 |
58 | &:before,
59 | &:after {
60 | width: 100%;
61 | transition: 600ms ease all;
62 | }
63 | }
64 |
65 | &:before,
66 | &:after {
67 | content: '';
68 | position: absolute;
69 | top: 0;
70 | right: 0;
71 | height: 2px;
72 | width: 0;
73 | transition: 400ms ease all;
74 | }
75 |
76 | &::after {
77 | right: inherit;
78 | top: inherit;
79 | left: 0;
80 | bottom: 0;
81 | }
82 | }
83 |
84 | .custom-button {
85 | display: inline-block;
86 | line-height: 1;
87 | white-space: nowrap;
88 | cursor: pointer;
89 | background: #fff;
90 | color: #fff;
91 | -webkit-appearance: none;
92 | text-align: center;
93 | box-sizing: border-box;
94 | outline: 0;
95 | margin: 0;
96 | padding: 10px 15px;
97 | font-size: 14px;
98 | border-radius: 4px;
99 | }
100 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/utils/log_util.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 |
5 | from loguru import logger as _logger
6 | from loguru._logger import Logger
7 |
8 | from middlewares.trace_middleware import TraceCtx
9 |
10 |
11 | class LoggerInitializer:
12 | def __init__(self) -> None:
13 | self.log_path = os.path.join(os.getcwd(), 'logs')
14 | self.__ensure_log_directory_exists()
15 | self.log_path_error = os.path.join(self.log_path, f'{time.strftime("%Y-%m-%d")}_error.log')
16 |
17 | def __ensure_log_directory_exists(self) -> None:
18 | """
19 | 确保日志目录存在,如果不存在则创建
20 | """
21 | if not os.path.exists(self.log_path):
22 | os.mkdir(self.log_path)
23 |
24 | @staticmethod
25 | def __filter(log: dict) -> dict:
26 | """
27 | 自定义日志过滤器,添加trace_id
28 | """
29 | log['trace_id'] = TraceCtx.get_id()
30 | return log
31 |
32 | def init_log(self) -> Logger:
33 | """
34 | 初始化日志配置
35 | """
36 | # 自定义日志格式
37 | format_str = (
38 | '{time:YYYY-MM-DD HH:mm:ss.SSS} | '
39 | '{trace_id} | '
40 | '{level: <8} | '
41 | '{name}:{function}:{line} - '
42 | '{message}'
43 | )
44 | _logger.remove()
45 | # 移除后重新添加sys.stderr, 目的: 控制台输出与文件日志内容和结构一致
46 | _logger.add(sys.stderr, filter=self.__filter, format=format_str, enqueue=True)
47 | _logger.add(
48 | self.log_path_error,
49 | filter=self.__filter,
50 | format=format_str,
51 | rotation='50MB',
52 | encoding='utf-8',
53 | enqueue=True,
54 | compression='zip',
55 | )
56 |
57 | return _logger
58 |
59 |
60 | # 初始化日志处理器
61 | log_initializer = LoggerInitializer()
62 | logger = log_initializer.init_log()
63 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/utils/scroll-to.js:
--------------------------------------------------------------------------------
1 | Math.easeInOutQuad = function(t, b, c, d) {
2 | t /= d / 2
3 | if (t < 1) {
4 | return c / 2 * t * t + b
5 | }
6 | t--
7 | return -c / 2 * (t * (t - 2) - 1) + b
8 | }
9 |
10 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
11 | var requestAnimFrame = (function() {
12 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
13 | })()
14 |
15 | /**
16 | * Because it's so fucking difficult to detect the scrolling element, just move them all
17 | * @param {number} amount
18 | */
19 | function move(amount) {
20 | document.documentElement.scrollTop = amount
21 | document.body.parentNode.scrollTop = amount
22 | document.body.scrollTop = amount
23 | }
24 |
25 | function position() {
26 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
27 | }
28 |
29 | /**
30 | * @param {number} to
31 | * @param {number} duration
32 | * @param {Function} callback
33 | */
34 | export function scrollTo(to, duration, callback) {
35 | const start = position()
36 | const change = to - start
37 | const increment = 20
38 | let currentTime = 0
39 | duration = (typeof (duration) === 'undefined') ? 500 : duration
40 | var animateScroll = function() {
41 | // increment the time
42 | currentTime += increment
43 | // find the value with the quadratic in-out easing function
44 | var val = Math.easeInOutQuad(currentTime, start, change, duration)
45 | // move the document.body
46 | move(val)
47 | // do the animation unless its over
48 | if (currentTime < duration) {
49 | requestAnimFrame(animateScroll)
50 | } else {
51 | if (callback && typeof (callback) === 'function') {
52 | // the animation is done so lets callback
53 | callback()
54 | }
55 | }
56 | }
57 | animateScroll()
58 | }
59 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/error/401.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 返回
5 |
6 |
7 |
8 |
9 | 401错误!
10 |
11 | 您没有访问权限!
12 | 对不起,您没有访问权限,请不要进行非法操作!您可以返回主页面
13 |
14 | -
15 |
16 | 回首页
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
43 |
44 |
83 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/plugins/modal.js:
--------------------------------------------------------------------------------
1 | import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
2 |
3 | let loadingInstance;
4 |
5 | export default {
6 | // 消息提示
7 | msg(content) {
8 | ElMessage.info(content)
9 | },
10 | // 错误消息
11 | msgError(content) {
12 | ElMessage.error(content)
13 | },
14 | // 成功消息
15 | msgSuccess(content) {
16 | ElMessage.success(content)
17 | },
18 | // 警告消息
19 | msgWarning(content) {
20 | ElMessage.warning(content)
21 | },
22 | // 弹出提示
23 | alert(content) {
24 | ElMessageBox.alert(content, "系统提示")
25 | },
26 | // 错误提示
27 | alertError(content) {
28 | ElMessageBox.alert(content, "系统提示", { type: 'error' })
29 | },
30 | // 成功提示
31 | alertSuccess(content) {
32 | ElMessageBox.alert(content, "系统提示", { type: 'success' })
33 | },
34 | // 警告提示
35 | alertWarning(content) {
36 | ElMessageBox.alert(content, "系统提示", { type: 'warning' })
37 | },
38 | // 通知提示
39 | notify(content) {
40 | ElNotification.info(content)
41 | },
42 | // 错误通知
43 | notifyError(content) {
44 | ElNotification.error(content);
45 | },
46 | // 成功通知
47 | notifySuccess(content) {
48 | ElNotification.success(content)
49 | },
50 | // 警告通知
51 | notifyWarning(content) {
52 | ElNotification.warning(content)
53 | },
54 | // 确认窗体
55 | confirm(content) {
56 | return ElMessageBox.confirm(content, "系统提示", {
57 | confirmButtonText: '确定',
58 | cancelButtonText: '取消',
59 | type: "warning",
60 | })
61 | },
62 | // 提交内容
63 | prompt(content) {
64 | return ElMessageBox.prompt(content, "系统提示", {
65 | confirmButtonText: '确定',
66 | cancelButtonText: '取消',
67 | type: "warning",
68 | })
69 | },
70 | // 打开遮罩层
71 | loading(content) {
72 | loadingInstance = ElLoading.service({
73 | lock: true,
74 | text: content,
75 | background: "rgba(0, 0, 0, 0.7)",
76 | })
77 | },
78 | // 关闭遮罩层
79 | closeLoading() {
80 | loadingInstance.close();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-backend/module_admin/entity/do/dept_do.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from sqlalchemy import CHAR, BigInteger, Column, DateTime, Integer, String
4 |
5 | from config.database import Base
6 | from config.env import DataBaseConfig
7 | from utils.common_util import SqlalchemyUtil
8 |
9 |
10 | class SysDept(Base):
11 | """
12 | 部门表
13 | """
14 |
15 | __tablename__ = 'sys_dept'
16 | __table_args__ = {'comment': '部门表'}
17 |
18 | dept_id = Column(BigInteger, primary_key=True, autoincrement=True, comment='部门id')
19 | parent_id = Column(BigInteger, server_default='0', comment='父部门id')
20 | ancestors = Column(String(50), nullable=True, server_default="''", comment='祖级列表')
21 | dept_name = Column(String(30), nullable=True, server_default="''", comment='部门名称')
22 | order_num = Column(Integer, server_default='0', comment='显示顺序')
23 | leader = Column(
24 | String(20),
25 | nullable=True,
26 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
27 | comment='负责人',
28 | )
29 | phone = Column(
30 | String(11),
31 | nullable=True,
32 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
33 | comment='联系电话',
34 | )
35 | email = Column(
36 | String(50),
37 | nullable=True,
38 | server_default=SqlalchemyUtil.get_server_default_null(DataBaseConfig.db_type),
39 | comment='邮箱',
40 | )
41 | status = Column(CHAR(1), nullable=True, server_default='0', comment='部门状态(0正常 1停用)')
42 | del_flag = Column(CHAR(1), nullable=True, server_default='0', comment='删除标志(0代表存在 2代表删除)')
43 | create_by = Column(String(64), nullable=True, server_default="''", comment='创建者')
44 | create_time = Column(DateTime, nullable=True, default=datetime.now(), comment='创建时间')
45 | update_by = Column(String(64), nullable=True, server_default="''", comment='更新者')
46 | update_time = Column(DateTime, nullable=True, default=datetime.now(), comment='更新时间')
47 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/views/tool/build/CodeTypeDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ item.label }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 取消
18 | 确定
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/docker-compose.my.yml:
--------------------------------------------------------------------------------
1 | services:
2 | # 前端服务
3 | ruoyi-frontend:
4 | build:
5 | context: ./ruoyi-fastapi-frontend
6 | dockerfile: Dockerfile
7 | image: ruoyi-frontend:latest
8 | container_name: ruoyi-frontend
9 | ports:
10 | - "12580:80"
11 | volumes:
12 | - ./ruoyi-fastapi-frontend/bin/nginx.dockermy.conf:/etc/nginx/conf.d/default.conf
13 | depends_on:
14 | - ruoyi-backend-my
15 | networks:
16 | - ruoyi-network
17 |
18 | # 后端服务(MySQL版本)
19 | ruoyi-backend-my:
20 | build:
21 | context: ./ruoyi-fastapi-backend
22 | dockerfile: Dockerfile.my
23 | image: ruoyi-backend-my:latest
24 | container_name: ruoyi-backend-my
25 | ports:
26 | - "19099:9099"
27 | depends_on:
28 | ruoyi-mysql:
29 | condition: service_healthy
30 | ruoyi-redis:
31 | condition: service_healthy
32 | networks:
33 | - ruoyi-network
34 |
35 | # MySQL服务
36 | ruoyi-mysql:
37 | image: mysql:8.0
38 | container_name: ruoyi-mysql
39 | environment:
40 | MYSQL_ROOT_PASSWORD: root
41 | MYSQL_DATABASE: ruoyi-fastapi
42 | ports:
43 | - "13306:3306"
44 | volumes:
45 | - ./ruoyi-fastapi-backend/sql/ruoyi-fastapi.sql:/docker-entrypoint-initdb.d/ruoyi-fastapi.sql
46 | command: --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --skip-character-set-client-handshake=1
47 | networks:
48 | - ruoyi-network
49 | healthcheck:
50 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
51 | interval: 5s
52 | timeout: 10s
53 | retries: 30
54 |
55 | # Redis服务
56 | ruoyi-redis:
57 | image: redis:latest
58 | container_name: ruoyi-redis
59 | ports:
60 | - "16379:6379"
61 | networks:
62 | - ruoyi-network
63 | healthcheck:
64 | test: ["CMD", "redis-cli", "ping"]
65 | interval: 5s
66 | timeout: 10s
67 | retries: 30
68 |
69 | # 网络配置
70 | networks:
71 | ruoyi-network:
72 | name: ruoyi-network
73 | driver: bridge
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/store/modules/settings.js:
--------------------------------------------------------------------------------
1 | import defaultSettings from '@/settings'
2 | import { useDark, useToggle } from '@vueuse/core'
3 | import { useDynamicTitle } from '@/utils/dynamicTitle'
4 |
5 | const isDark = useDark()
6 | const toggleDark = useToggle(isDark)
7 |
8 | const { sideTheme, showSettings, navType, tagsView, tagsIcon, fixedHeader, sidebarLogo, dynamicTitle, footerVisible, footerContent } = defaultSettings
9 |
10 | const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
11 |
12 | const useSettingsStore = defineStore(
13 | 'settings',
14 | {
15 | state: () => ({
16 | title: '',
17 | theme: storageSetting.theme || '#409EFF',
18 | sideTheme: storageSetting.sideTheme || sideTheme,
19 | showSettings: showSettings,
20 | navType: storageSetting.navType === undefined ? navType : storageSetting.navType,
21 | tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
22 | tagsIcon: storageSetting.tagsIcon === undefined ? tagsIcon : storageSetting.tagsIcon,
23 | fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
24 | sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
25 | dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle,
26 | footerVisible: storageSetting.footerVisible === undefined ? footerVisible : storageSetting.footerVisible,
27 | footerContent: footerContent,
28 | isDark: isDark.value
29 | }),
30 | actions: {
31 | // 修改布局设置
32 | changeSetting(data) {
33 | const { key, value } = data
34 | if (this.hasOwnProperty(key)) {
35 | this[key] = value
36 | }
37 | },
38 | // 设置网页标题
39 | setTitle(title) {
40 | this.title = title
41 | useDynamicTitle()
42 | },
43 | // 切换暗黑模式
44 | toggleTheme() {
45 | this.isDark = !this.isDark
46 | toggleDark()
47 | }
48 | }
49 | })
50 |
51 | export default useSettingsStore
52 |
--------------------------------------------------------------------------------
/ruoyi-fastapi-frontend/src/assets/icons/svg/date-range.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------