Browse Source

!1 RuoYi-Vue3-FastAPI v1.0.0

Merge pull request !1 from insistence/develop
master
insistence 1 year ago
committed by Gitee
parent
commit
733837d6da
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 36
      README.en.md
  2. 199
      README.md
  3. 50
      ruoyi-fastapi-backend/.env.dev
  4. 50
      ruoyi-fastapi-backend/.env.prod
  5. 123
      ruoyi-fastapi-backend/app.py
  6. BIN
      ruoyi-fastapi-backend/caches/profile/avatar/blob_20240119210836.jpeg
  7. 4
      ruoyi-fastapi-backend/config/database.py
  8. 143
      ruoyi-fastapi-backend/config/env.py
  9. 24
      ruoyi-fastapi-backend/config/get_redis.py
  10. 10
      ruoyi-fastapi-backend/config/get_scheduler.py
  11. 28
      ruoyi-fastapi-backend/exceptions/exception.py
  12. 27
      ruoyi-fastapi-backend/exceptions/handle.py
  13. 19
      ruoyi-fastapi-backend/middlewares/cors_middleware.py
  14. 10
      ruoyi-fastapi-backend/middlewares/handle.py
  15. 2
      ruoyi-fastapi-backend/module_admin/aspect/interface_auth.py
  16. 4
      ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py
  17. 10
      ruoyi-fastapi-backend/module_admin/controller/config_controller.py
  18. 24
      ruoyi-fastapi-backend/module_admin/controller/dict_controller.py
  19. 24
      ruoyi-fastapi-backend/module_admin/controller/job_controller.py
  20. 20
      ruoyi-fastapi-backend/module_admin/controller/log_controller.py
  21. 74
      ruoyi-fastapi-backend/module_admin/controller/login_controller.py
  22. 7
      ruoyi-fastapi-backend/module_admin/controller/notice_controller.py
  23. 10
      ruoyi-fastapi-backend/module_admin/controller/post_controler.py
  24. 20
      ruoyi-fastapi-backend/module_admin/controller/role_controller.py
  25. 12
      ruoyi-fastapi-backend/module_admin/controller/user_controller.py
  26. 9
      ruoyi-fastapi-backend/module_admin/dao/config_dao.py
  27. 17
      ruoyi-fastapi-backend/module_admin/dao/dict_dao.py
  28. 9
      ruoyi-fastapi-backend/module_admin/dao/job_dao.py
  29. 9
      ruoyi-fastapi-backend/module_admin/dao/job_log_dao.py
  30. 18
      ruoyi-fastapi-backend/module_admin/dao/log_dao.py
  31. 9
      ruoyi-fastapi-backend/module_admin/dao/notice_dao.py
  32. 9
      ruoyi-fastapi-backend/module_admin/dao/post_dao.py
  33. 9
      ruoyi-fastapi-backend/module_admin/dao/role_dao.py
  34. 47
      ruoyi-fastapi-backend/module_admin/dao/user_dao.py
  35. 4
      ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py
  36. 10
      ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py
  37. 8
      ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py
  38. 8
      ruoyi-fastapi-backend/module_admin/entity/vo/log_vo.py
  39. 11
      ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py
  40. 4
      ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py
  41. 4
      ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py
  42. 4
      ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py
  43. 8
      ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py
  44. 13
      ruoyi-fastapi-backend/module_admin/service/config_service.py
  45. 14
      ruoyi-fastapi-backend/module_admin/service/dict_service.py
  46. 28
      ruoyi-fastapi-backend/module_admin/service/job_log_service.py
  47. 7
      ruoyi-fastapi-backend/module_admin/service/job_service.py
  48. 14
      ruoyi-fastapi-backend/module_admin/service/log_service.py
  49. 81
      ruoyi-fastapi-backend/module_admin/service/login_service.py
  50. 7
      ruoyi-fastapi-backend/module_admin/service/notice_service.py
  51. 2
      ruoyi-fastapi-backend/module_admin/service/online_service.py
  52. 7
      ruoyi-fastapi-backend/module_admin/service/post_service.py
  53. 34
      ruoyi-fastapi-backend/module_admin/service/role_service.py
  54. 20
      ruoyi-fastapi-backend/module_admin/service/user_service.py
  55. 15
      ruoyi-fastapi-backend/requirements.txt
  56. 83
      ruoyi-fastapi-backend/server.py
  57. 10
      ruoyi-fastapi-backend/sub_applications/handle.py
  58. 10
      ruoyi-fastapi-backend/sub_applications/staticfiles.py
  59. 6
      ruoyi-fastapi-backend/utils/common_util.py
  60. 100
      ruoyi-fastapi-backend/utils/page_util.py
  61. 112
      ruoyi-fastapi-backend/utils/response_util.py
  62. 8
      ruoyi-fastapi-frontend/package.json
  63. 2
      ruoyi-fastapi-frontend/src/components/RuoYi/Git/index.vue
  64. 2
      ruoyi-fastapi-frontend/src/router/index.js
  65. 68
      ruoyi-fastapi-frontend/src/views/dashboard/editable-link-group.vue
  66. 738
      ruoyi-fastapi-frontend/src/views/dashboard/index.vue
  67. 1061
      ruoyi-fastapi-frontend/src/views/index.vue
  68. 5
      ruoyi-fastapi-frontend/src/views/login.vue
  69. 4
      ruoyi-fastapi-frontend/src/views/register.vue

36
README.en.md

@ -1,36 +0,0 @@
# RuoYi-Vue3-FastAPI
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

199
README.md

@ -1,39 +1,184 @@
# RuoYi-Vue3-FastAPI
<p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.0.0</h1>
<h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4>
<p align="center">
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/stargazers"><img src="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/badge/star.svg?theme=dark"></a>
<a href="https://github.com/insistence/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/github/stars/insistence/RuoYi-Vue3-FastAPI?style=social"></a>
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/badge/RuoYiVue3FastAPI-v1.0.0-brightgreen.svg"></a>
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
<img src="https://img.shields.io/badge/python-≥3.8-blue">
<img src="https://img.shields.io/badge/MySQL-≥5.7-blue">
</p>
#### 介绍
{**以下是 Gitee 平台说明,您可以替换此简介**
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
## 平台简介
#### 软件架构
软件架构说明
RuoYi-Vue-FastAPI是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 前端采用Vue、Element Plus,基于<u>[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3)</u>前端项目修改。
* 后端采用FastAPI、sqlalchemy、MySQL、Redis、OAuth2 & Jwt。
* 权限认证使用OAuth2 & Jwt,支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* Vue2版本:
- Gitte仓库地址:https://gitee.com/insistence2022/RuoYi-Vue-FastAPI。
- GitHub仓库地址:https://github.com/insistence/RuoYi-Vue-FastAPI。
* 纯Python版本:
- Gitte仓库地址:https://gitee.com/insistence2022/dash-fastapi-admin。
- GitHub仓库地址:https://github.com/insistence/Dash-FastAPI-Admin。
* 特别鸣谢:<u>[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3)</u>
#### 安装教程
## 内置功能
1. xxxx
2. xxxx
3. xxxx
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
3. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
4. 部门管理:配置系统组织机构(公司、部门、小组)。
5. 岗位管理:配置系统用户所属担任职务。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
14. 缓存监控:对系统的缓存信息查询,命令统计等。
15. 系统接口:根据业务代码自动生成相关的api接口文档。
#### 使用说明
## 演示图
1. xxxx
2. xxxx
3. xxxx
<table>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/login.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dashboard.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/user.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/role.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/menu.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dept.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/post.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dict.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/config.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/notice.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/operLog.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/loginLog.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/online.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/job.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/server.png"/></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/cache.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/cacheList.png"></td>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/api.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/profile.png"/></td>
</tr>
</table>
#### 参与贡献
## 在线体验
- *账号:admin*
- *密码:admin123*
- 演示地址:<a href="https://vfadmin.insistence.tech">vfadmin管理系统<a>
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
## 项目开发及发布相关
### 开发
#### 特技
```bash
# 克隆项目
git clone https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
# 进入项目根目录
cd RuoYi-Vue3-FastAPI
```
#### 前端
```bash
# 进入前端目录
cd ruoyi-fastapi-frontend
# 安装依赖
npm install 或 yarn --registry=https://registry.npmmirror.com
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npmmirror.com
# 启动服务
npm run dev 或 yarn dev
```
#### 后端
```bash
# 进入后端目录
cd ruoyi-fastapi-backend
# 安装项目依赖环境
pip3 install -r requirements.txt
# 配置环境
在.env.dev文件中配置开发环境的数据库和redis
# 运行sql文件
1.新建数据库ruoyi-fastapi(默认,可修改)
2.使用命令或数据库连接工具运行sql文件夹下的ruoyi-fastapi.sql
# 运行后端
python3 app.py --env=dev
```
#### 访问
```bash
# 默认账号密码
账号:admin
密码:admin123
# 浏览器访问
地址:http://localhost:80
```
### 发布
#### 前端
```bash
# 构建测试环境
npm run build:stage 或 yarn build:stage
# 构建生产环境
npm run build:prod 或 yarn build:prod
```
#### 后端
```bash
# 配置环境
在.env.prod文件中配置生产环境的数据库和redis
# 运行后端
python3 app.py --env=prod
```
## 交流与赞助
如果有对本项目及FastAPI感兴趣的朋友,欢迎加入知识星球一起交流学习,让我们一起变得更强。如果你觉得这个项目帮助到了你,你可以请作者喝杯咖啡表示鼓励☕。扫描下面微信二维码添加微信备注VF-Admin即可进群。
<table>
<tr>
<td><img alt="zsxq" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/zsxq.jpg"></td>
<td><img alt="zanzhu" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/zanzhu.jpg"></td>
</tr>
<tr>
<td><img alt="wxcode" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/wxcode.jpg"></td>
</tr>
</table>

50
ruoyi-fastapi-backend/.env.dev

@ -0,0 +1,50 @@
# -------- 应用配置 --------
# 应用运行环境
APP_ENV = 'dev'
# 应用名称
APP_NAME = 'RuoYi-FasAPI'
# 应用代理路径
APP_ROOT_PATH = '/dev-api'
# 应用主机
APP_HOST = '0.0.0.0'
# 应用端口
APP_PORT = 9099
# 应用版本
APP_VERSION= '1.0.0'
# 应用是否开启热重载
APP_RELOAD = true
# -------- Jwt配置 --------
# Jwt秘钥
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
# Jwt算法
JWT_ALGORITHM = 'HS256'
# 令牌过期时间
JWT_EXPIRE_MINUTES = 1440
# redis中令牌过期时间
JWT_REDIS_EXPIRE_MINUTES = 30
# -------- 数据库配置 --------
# 数据库主机
DB_HOST = '127.0.0.1'
# 数据库端口
DB_PORT = 3306
# 数据库用户名
DB_USERNAME = 'root'
# 数据库密码
DB_PASSWORD = 'mysqlroot'
# 数据库名称
DB_DATABASE = 'ruoyi-fastapi'
# -------- Redis配置 --------
# Redis主机
REDIS_HOST = '127.0.0.1'
# Redis端口
REDIS_PORT = 6379
# Redis用户名
REDIS_USERNAME = ''
# Redis密码
REDIS_PASSWORD = ''
# Redis数据库
REDIS_DATABASE = 2

50
ruoyi-fastapi-backend/.env.prod

@ -0,0 +1,50 @@
# -------- 应用配置 --------
# 应用运行环境
APP_ENV = 'prod'
# 应用名称
APP_NAME = 'RuoYi-FasAPI'
# 应用代理路径
APP_ROOT_PATH = '/prod-api'
# 应用主机
APP_HOST = '0.0.0.0'
# 应用端口
APP_PORT = 9099
# 应用版本
APP_VERSION= '1.0.0'
# 应用是否开启热重载
APP_RELOAD = false
# -------- Jwt配置 --------
# Jwt秘钥
JWT_SECRET_KEY = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
# Jwt算法
JWT_ALGORITHM = 'HS256'
# 令牌过期时间
JWT_EXPIRE_MINUTES = 1440
# redis中令牌过期时间
JWT_REDIS_EXPIRE_MINUTES = 30
# -------- 数据库配置 --------
# 数据库主机
DB_HOST = '127.0.0.1'
# 数据库端口
DB_PORT = 3306
# 数据库用户名
DB_USERNAME = 'root'
# 数据库密码
DB_PASSWORD = 'root'
# 数据库名称
DB_DATABASE = 'ruoyi-fastapi'
# -------- Redis配置 --------
# Redis主机
REDIS_HOST = '127.0.0.1'
# Redis端口
REDIS_PORT = 6379
# Redis用户名
REDIS_USERNAME = ''
# Redis密码
REDIS_PASSWORD = ''
# Redis数据库
REDIS_DATABASE = 2

123
ruoyi-fastapi-backend/app.py

@ -1,119 +1,12 @@
from fastapi import FastAPI, Request
from fastapi.exceptions import HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
import uvicorn
from contextlib import asynccontextmanager
from module_admin.controller.login_controller import loginController
from module_admin.controller.captcha_controller import captchaController
from module_admin.controller.user_controller import userController
from module_admin.controller.menu_controller import menuController
from module_admin.controller.dept_controller import deptController
from module_admin.controller.role_controller import roleController
from module_admin.controller.post_controler import postController
from module_admin.controller.dict_controller import dictController
from module_admin.controller.config_controller import configController
from module_admin.controller.notice_controller import noticeController
from module_admin.controller.log_controller import logController
from module_admin.controller.online_controller import onlineController
from module_admin.controller.job_controller import jobController
from module_admin.controller.server_controller import serverController
from module_admin.controller.cache_controller import cacheController
from module_admin.controller.common_controller import commonController
from config.env import UploadConfig
from config.get_redis import RedisUtil
from config.get_db import init_create_table
from config.get_scheduler import SchedulerUtil
from utils.response_util import *
from utils.log_util import logger
from utils.common_util import worship
from server import app, AppConfig
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("RuoYi-FastAPI开始启动")
worship()
await init_create_table()
app.state.redis = await RedisUtil.create_redis_pool()
await RedisUtil.init_sys_dict(app.state.redis)
await RedisUtil.init_sys_config(app.state.redis)
await SchedulerUtil.init_system_scheduler()
logger.info("RuoYi-FastAPI启动成功")
yield
await RedisUtil.close_redis_pool(app)
await SchedulerUtil.close_system_scheduler()
app = FastAPI(
title='RuoYi-FastAPI',
description='RuoYi-FastAPI接口文档',
version='1.0.0',
lifespan=lifespan
)
# 前端页面url
origins = [
"http://localhost:81",
"http://127.0.0.1:81",
]
# 后台api允许跨域
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 实例化UploadConfig,确保应用启动时上传目录存在
upload_config = UploadConfig()
# 挂载静态文件路径
app.mount(f"{upload_config.UPLOAD_PREFIX}", StaticFiles(directory=f"{upload_config.UPLOAD_PATH}"), name="profile")
# 自定义token检验异常
@app.exception_handler(AuthException)
async def auth_exception_handler(request: Request, exc: AuthException):
return ResponseUtil.unauthorized(data=exc.data, msg=exc.message)
# 自定义权限检验异常
@app.exception_handler(PermissionException)
async def permission_exception_handler(request: Request, exc: PermissionException):
return ResponseUtil.forbidden(data=exc.data, msg=exc.message)
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
content=jsonable_encoder({"message": exc.detail, "code": exc.status_code}),
status_code=exc.status_code
)
controller_list = [
{'router': loginController, 'tags': ['登录模块']},
{'router': captchaController, 'tags': ['验证码模块']},
{'router': userController, 'tags': ['系统管理-用户管理']},
{'router': roleController, 'tags': ['系统管理-角色管理']},
{'router': menuController, 'tags': ['系统管理-菜单管理']},
{'router': deptController, 'tags': ['系统管理-部门管理']},
{'router': postController, 'tags': ['系统管理-岗位管理']},
{'router': dictController, 'tags': ['系统管理-字典管理']},
{'router': configController, 'tags': ['系统管理-参数管理']},
{'router': noticeController, 'tags': ['系统管理-通知公告管理']},
{'router': logController, 'tags': ['系统管理-日志管理']},
{'router': onlineController, 'tags': ['系统监控-在线用户']},
{'router': jobController, 'tags': ['系统监控-定时任务']},
{'router': serverController, 'tags': ['系统监控-菜单管理']},
{'router': cacheController, 'tags': ['系统监控-缓存监控']},
{'router': commonController, 'tags': ['通用模块']}
]
for controller in controller_list:
app.include_router(router=controller.get('router'), tags=controller.get('tags'))
if __name__ == '__main__':
uvicorn.run(app='app:app', host="0.0.0.0", port=9099, root_path='/dev-api', reload=True)
uvicorn.run(
app='app:app',
host=AppConfig.app_host,
port=AppConfig.app_port,
root_path=AppConfig.app_root_path,
reload=AppConfig.app_reload
)

BIN
ruoyi-fastapi-backend/caches/profile/avatar/blob_20240119210836.jpeg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

4
ruoyi-fastapi-backend/config/database.py

@ -4,8 +4,8 @@ from sqlalchemy.orm import sessionmaker
from urllib.parse import quote_plus
from config.env import DataBaseConfig
SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.USERNAME}:{quote_plus(DataBaseConfig.PASSWORD)}@" \
f"{DataBaseConfig.HOST}:{DataBaseConfig.PORT}/{DataBaseConfig.DB}"
SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@" \
f"{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, echo=True

143
ruoyi-fastapi-backend/config/env.py

@ -1,39 +1,57 @@
import os
import sys
import argparse
from pydantic_settings import BaseSettings
from functools import lru_cache
from dotenv import load_dotenv
class JwtConfig:
class AppSettings(BaseSettings):
"""
应用配置
"""
app_env: str = 'dev'
app_name: str = 'RuoYi-FasAPI'
app_root_path: str = '/dev-api'
app_host: str = '0.0.0.0'
app_port: int = 9099
app_version: str = '1.0.0'
app_reload: bool = True
class JwtSettings(BaseSettings):
"""
Jwt配置
"""
SECRET_KEY = "b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 1440
REDIS_TOKEN_EXPIRE_MINUTES = 30
jwt_secret_key: str = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
jwt_algorithm: str = 'HS256'
jwt_expire_minutes: int = 1440
jwt_redis_expire_minutes: int = 30
class DataBaseConfig:
class DataBaseSettings(BaseSettings):
"""
数据库配置
"""
HOST = "127.0.0.1"
PORT = 3306
USERNAME = 'root'
PASSWORD = 'mysqlroot'
DB = 'ruoyi-fastapi'
db_host: str = '127.0.0.1'
db_port: int = 3306
db_username: str = 'root'
db_password: str = 'mysqlroot'
db_database: str = 'ruoyi-fastapi'
class RedisConfig:
class RedisSettings(BaseSettings):
"""
Redis配置
"""
HOST = "127.0.0.1"
PORT = 6379
USERNAME = ''
PASSWORD = ''
DB = 2
redis_host: str = '127.0.0.1'
redis_port: int = 6379
redis_username: str = ''
redis_password: str = ''
redis_database: int = 2
class UploadConfig:
class UploadSettings:
"""
上传配置
"""
@ -80,3 +98,92 @@ class RedisInitKeyConfig:
ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'}
PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'}
SMS_CODE = {'key': 'sms_code', 'remark': '短信验证码'}
class GetConfig:
"""
获取配置
"""
def __init__(self):
self.parse_cli_args()
@lru_cache()
def get_app_config(self):
"""
获取应用配置
"""
# 实例化应用配置模型
return AppSettings()
@lru_cache()
def get_jwt_config(self):
"""
获取Jwt配置
"""
# 实例化Jwt配置模型
return JwtSettings()
@lru_cache()
def get_database_config(self):
"""
获取数据库配置
"""
# 实例化数据库配置模型
return DataBaseSettings()
@lru_cache()
def get_redis_config(self):
"""
获取Redis配置
"""
# 实例化Redis配置模型
return RedisSettings()
@lru_cache()
def get_upload_config(self):
"""
获取数据库配置
"""
# 实例上传配置
return UploadSettings()
@staticmethod
def parse_cli_args():
"""
解析命令行参数
"""
if 'uvicorn' in sys.argv[0]:
# 使用uvicorn启动时,命令行参数需要按照uvicorn的文档进行配置,无法自定义参数
pass
else:
# 使用argparse定义命令行参数
parser = argparse.ArgumentParser(description='命令行参数')
parser.add_argument('--env', type=str, default='', help='运行环境')
# 解析命令行参数
args = parser.parse_args()
# 设置环境变量,如果未设置命令行参数,默认APP_ENV为dev
os.environ['APP_ENV'] = args.env if args.env else 'dev'
# 读取运行环境
run_env = os.environ.get('APP_ENV', '')
# 运行环境未指定时默认加载.env.dev
env_file = '.env.dev'
# 运行环境不为空时按命令行参数加载对应.env文件
if run_env != '':
env_file = f'.env.{run_env}'
# 加载配置
load_dotenv(env_file)
# 实例化获取配置类
get_config = GetConfig()
# 应用配置
AppConfig = get_config.get_app_config()
# Jwt配置
JwtConfig = get_config.get_jwt_config()
# 数据库配置
DataBaseConfig = get_config.get_database_config()
# Redis配置
RedisConfig = get_config.get_redis_config()
# 上传配置
UploadConfig = get_config.get_upload_config()

24
ruoyi-fastapi-backend/config/get_redis.py

@ -1,4 +1,5 @@
import aioredis
from redis import asyncio as aioredis
from redis.exceptions import AuthenticationError, TimeoutError, RedisError
from module_admin.service.dict_service import DictDataService
from module_admin.service.config_service import ConfigService
from config.env import RedisConfig
@ -19,15 +20,26 @@ class RedisUtil:
"""
logger.info("开始连接redis...")
redis = await aioredis.from_url(
url=f"redis://{RedisConfig.HOST}",
port=RedisConfig.PORT,
username=RedisConfig.USERNAME,
password=RedisConfig.PASSWORD,
db=RedisConfig.DB,
url=f"redis://{RedisConfig.redis_host}",
port=RedisConfig.redis_port,
username=RedisConfig.redis_username,
password=RedisConfig.redis_password,
db=RedisConfig.redis_database,
encoding="utf-8",
decode_responses=True
)
try:
connection = await redis.ping()
if connection:
logger.info("redis连接成功")
else:
logger.error("redis连接失败")
except AuthenticationError as e:
logger.error(f"redis用户名或密码错误,详细错误信息:{e}")
except TimeoutError as e:
logger.error(f"redis连接超时,详细错误信息:{e}")
except RedisError as e:
logger.error(f"redis连接错误,详细错误信息:{e}")
return redis
@classmethod

10
ruoyi-fastapi-backend/config/get_scheduler.py

@ -70,11 +70,11 @@ job_stores = {
'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine),
'redis': RedisJobStore(
**dict(
host=RedisConfig.HOST,
port=RedisConfig.PORT,
username=RedisConfig.USERNAME,
password=RedisConfig.PASSWORD,
db=RedisConfig.DB
host=RedisConfig.redis_host,
port=RedisConfig.redis_port,
username=RedisConfig.redis_username,
password=RedisConfig.redis_password,
db=RedisConfig.redis_database
)
)
}

28
ruoyi-fastapi-backend/exceptions/exception.py

@ -0,0 +1,28 @@
class LoginException(Exception):
"""
自定义登录异常LoginException
"""
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message
class AuthException(Exception):
"""
自定义令牌异常AuthException
"""
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message
class PermissionException(Exception):
"""
自定义权限异常PermissionException
"""
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message

27
ruoyi-fastapi-backend/exceptions/handle.py

@ -0,0 +1,27 @@
from fastapi import FastAPI, Request
from fastapi.exceptions import HTTPException
from exceptions.exception import AuthException, PermissionException
from utils.response_util import ResponseUtil, JSONResponse, jsonable_encoder
def handle_exception(app: FastAPI):
"""
全局异常处理
"""
# 自定义token检验异常
@app.exception_handler(AuthException)
async def auth_exception_handler(request: Request, exc: AuthException):
return ResponseUtil.unauthorized(data=exc.data, msg=exc.message)
# 自定义权限检验异常
@app.exception_handler(PermissionException)
async def permission_exception_handler(request: Request, exc: PermissionException):
return ResponseUtil.forbidden(data=exc.data, msg=exc.message)
# 处理其他http请求异常
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
content=jsonable_encoder({"code": exc.status_code, "msg": exc.detail}),
status_code=exc.status_code
)

19
ruoyi-fastapi-backend/middlewares/cors_middleware.py

@ -0,0 +1,19 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
def add_cors_middleware(app: FastAPI):
# 前端页面url
origins = [
"http://localhost:80",
"http://127.0.0.1:80",
]
# 后台api允许跨域
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

10
ruoyi-fastapi-backend/middlewares/handle.py

@ -0,0 +1,10 @@
from fastapi import FastAPI
from middlewares.cors_middleware import add_cors_middleware
def handle_middleware(app: FastAPI):
"""
全局中间件处理
"""
# 加载跨域中间件
add_cors_middleware(app)

2
ruoyi-fastapi-backend/module_admin/aspect/interface_auth.py

@ -1,7 +1,7 @@
from fastapi import Depends
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService
from utils.response_util import PermissionException
from exceptions.exception import PermissionException
class CheckUserInterfaceAuth:

4
ruoyi-fastapi-backend/module_admin/controller/captcha_controller.py

@ -15,6 +15,8 @@ captchaController = APIRouter()
async def get_captcha_image(request: Request):
try:
captcha_enabled = True if await request.app.state.redis.get(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == 'true' else False
register_enabled = True if await request.app.state.redis.get(
f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.registerUser") == 'true' else False
session_id = str(uuid.uuid4())
captcha_result = CaptchaService.create_captcha_image_service()
image = captcha_result[0]
@ -22,7 +24,7 @@ async def get_captcha_image(request: Request):
await request.app.state.redis.set(f"{RedisInitKeyConfig.CAPTCHA_CODES.get('key')}:{session_id}", computed_result, ex=timedelta(minutes=2))
logger.info(f'编号为{session_id}的会话获取图片验证码成功')
return ResponseUtil.success(
model_content=CaptchaCode(captchaEnabled=captcha_enabled, img=image, uuid=session_id)
model_content=CaptchaCode(captchaEnabled=captcha_enabled, registerEnabled=register_enabled, img=image, uuid=session_id)
)
except Exception as e:
logger.exception(e)

10
ruoyi-fastapi-backend/module_admin/controller/config_controller.py

@ -17,11 +17,8 @@ configController = APIRouter(prefix='/system/config', dependencies=[Depends(Logi
@configController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:list'))])
async def get_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True))
# 获取全量数据
config_query_result = ConfigService.get_config_list_services(query_db, config_query)
# 分页操作
config_page_query_result = get_page_obj(config_query_result, config_page_query.page_num, config_page_query.page_size)
# 获取分页数据
config_page_query_result = ConfigService.get_config_list_services(query_db, config_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=config_page_query_result)
except Exception as e:
@ -125,9 +122,8 @@ async def query_system_config(request: Request, config_key: str):
@log_decorator(title='参数管理', business_type=5)
async def export_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True))
# 获取全量数据
config_query_result = ConfigService.get_config_list_services(query_db, config_query)
config_query_result = ConfigService.get_config_list_services(query_db, config_page_query, is_page=False)
config_export_result = ConfigService.export_config_list_services(config_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(config_export_result))

24
ruoyi-fastapi-backend/module_admin/controller/dict_controller.py

@ -5,7 +5,7 @@ from module_admin.service.login_service import LoginService, CurrentUserModel
from module_admin.service.dict_service import *
from utils.response_util import *
from utils.log_util import *
from utils.page_util import *
from utils.page_util import PageResponseModel
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator
@ -17,11 +17,8 @@ dictController = APIRouter(prefix='/system/dict', dependencies=[Depends(LoginSer
@dictController.get("/type/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
async def get_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True))
# 获取全量数据
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query)
# 分页操作
dict_type_page_query_result = get_page_obj(dict_type_query_result, dict_type_page_query.page_num, dict_type_page_query.page_size)
# 获取分页数据
dict_type_page_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=dict_type_page_query_result)
except Exception as e:
@ -101,7 +98,7 @@ async def delete_system_dict_type(request: Request, dict_ids: str, query_db: Ses
@dictController.get("/type/optionselect", response_model=List[DictTypeModel], dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))])
async def query_system_dict_type_options(request: Request, query_db: Session = Depends(get_db)):
try:
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, DictTypeQueryModel(**dict()))
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, DictTypePageQueryModel(**dict()), is_page=False)
logger.info(f'获取成功')
return ResponseUtil.success(data=dict_type_query_result)
except Exception as e:
@ -124,9 +121,8 @@ async def query_detail_system_dict_type(request: Request, dict_id: int, query_db
@log_decorator(title='字典管理', business_type=5)
async def export_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True))
# 获取全量数据
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query)
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_page_query, is_page=False)
dict_type_export_result = DictTypeService.export_dict_type_list_services(dict_type_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result))
@ -150,11 +146,8 @@ async def query_system_dict_type_data(request: Request, dict_type: str, query_db
@dictController.get("/data/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
async def get_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True))
# 获取全量数据
dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query)
# 分页操作
dict_data_page_query_result = get_page_obj(dict_data_query_result, dict_data_page_query.page_num, dict_data_page_query.page_size)
# 获取分页数据
dict_data_page_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=dict_data_page_query_result)
except Exception as e:
@ -230,9 +223,8 @@ async def query_detail_system_dict_data(request: Request, dict_code: int, query_
@log_decorator(title='字典管理', business_type=5)
async def export_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True))
# 获取全量数据
dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query)
dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_page_query, is_page=False)
dict_data_export_result = DictDataService.export_dict_data_list_services(dict_data_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(dict_data_export_result))

24
ruoyi-fastapi-backend/module_admin/controller/job_controller.py

@ -18,11 +18,8 @@ jobController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.
@jobController.get("/job/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))])
async def get_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
job_query = JobModel(**job_page_query.model_dump(by_alias=True))
# 获取全量数据
job_query_result = JobService.get_job_list_services(query_db, job_query)
# 分页操作
notice_page_query_result = get_page_obj(job_query_result, job_page_query.page_num, job_page_query.page_size)
# 获取分页数据
notice_page_query_result = JobService.get_job_list_services(query_db, job_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=notice_page_query_result)
except Exception as e:
@ -133,9 +130,8 @@ async def query_detail_system_job(request: Request, job_id: int, query_db: Sessi
@log_decorator(title='定时任务管理', business_type=5)
async def export_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
job_query = JobModel(**job_page_query.model_dump(by_alias=True))
# 获取全量数据
job_query_result = JobService.get_job_list_services(query_db, job_query)
job_query_result = JobService.get_job_list_services(query_db, job_page_query, is_page=False)
job_export_result = await JobService.export_job_list_services(request, job_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(job_export_result))
@ -147,13 +143,10 @@ async def export_system_job_list(request: Request, job_page_query: JobPageQueryM
@jobController.get("/jobLog/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))])
async def get_system_job_log_list(request: Request, job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True))
# 获取全量数据
job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query)
# 分页操作
notice_page_query_result = get_page_obj(job_log_query_result, job_log_page_query.page_num, job_log_page_query.page_size)
# 获取分页数据
job_log_page_query_result = JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=notice_page_query_result)
return ResponseUtil.success(model_content=job_log_page_query_result)
except Exception as e:
logger.exception(e)
return ResponseUtil.error(msg=str(e))
@ -196,10 +189,9 @@ async def clear_system_job_log(request: Request, query_db: Session = Depends(get
@log_decorator(title='定时任务日志管理', business_type=5)
async def export_system_job_log_list(request: Request, job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True))
# 获取全量数据
job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query)
job_log_export_result = JobLogService.export_job_log_list_services(query_db, job_log_query_result)
job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False)
job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result))
except Exception as e:

20
ruoyi-fastapi-backend/module_admin/controller/log_controller.py

@ -17,11 +17,8 @@ logController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.
@logController.get("/operlog/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:list'))])
async def get_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True))
# 获取全量数据
operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query)
# 分页操作
operation_log_page_query_result = get_page_obj(operation_log_query_result, operation_log_page_query.page_num, operation_log_page_query.page_size)
# 获取分页数据
operation_log_page_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=operation_log_page_query_result)
except Exception as e:
@ -66,9 +63,8 @@ async def delete_system_operation_log(request: Request, oper_ids: str, query_db:
@log_decorator(title='操作日志管理', business_type=5)
async def export_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True))
# 获取全量数据
operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query)
operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_page_query, is_page=False)
operation_log_export_result = await OperationLogService.export_operation_log_list_services(request, operation_log_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result))
@ -80,11 +76,8 @@ async def export_system_operation_log_list(request: Request, operation_log_page_
@logController.get("/logininfor/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:list'))])
async def get_system_login_log_list(request: Request, login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True))
# 获取全量数据
login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query)
# 分页操作
login_log_page_query_result = get_page_obj(login_log_query_result, login_log_page_query.page_num, login_log_page_query.page_size)
# 获取分页数据
login_log_page_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=login_log_page_query_result)
except Exception as e:
@ -146,9 +139,8 @@ async def clear_system_login_log(request: Request, user_name: str, query_db: Ses
@log_decorator(title='登录日志管理', business_type=5)
async def export_system_login_log_list(request: Request, login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True))
# 获取全量数据
login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query)
login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_page_query, is_page=False)
login_log_export_result = LoginLogService.export_login_log_list_services(login_log_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(login_log_export_result))

74
ruoyi-fastapi-backend/module_admin/controller/login_controller.py

@ -2,10 +2,10 @@ from fastapi import APIRouter
from module_admin.service.login_service import *
from module_admin.entity.vo.login_vo import *
from module_admin.dao.login_dao import *
from module_admin.annotation.log_annotation import log_decorator
from config.env import JwtConfig, RedisInitKeyConfig
from utils.response_util import *
from utils.response_util import ResponseUtil
from utils.log_util import *
from module_admin.annotation.log_annotation import log_decorator
from datetime import timedelta
@ -29,7 +29,7 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D
except LoginException as e:
return ResponseUtil.failure(msg=e.message)
try:
access_token_expires = timedelta(minutes=JwtConfig.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token_expires = timedelta(minutes=JwtConfig.jwt_expire_minutes)
session_id = str(uuid.uuid4())
access_token = LoginService.create_access_token(
data={
@ -42,10 +42,11 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D
expires_delta=access_token_expires
)
await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", access_token,
ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
# 此方法可实现同一账号同一时间只能登录一次
# await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{result[0].user_id}", access_token,
# ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
# ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
UserService.edit_user_services(query_db, EditUserModel(userId=result[0].user_id, loginDate=datetime.now(), type='status'))
logger.info('登录成功')
# 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
@ -82,42 +83,57 @@ async def get_login_user_routers(request: Request, current_user: CurrentUserMode
return ResponseUtil.error(msg=str(e))
@loginController.post("/getSmsCode", response_model=SmsCode)
async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)):
@loginController.post("/register", response_model=CrudResponseModel)
async def register_user(request: Request, user_register: UserRegister, query_db: Session = Depends(get_db)):
try:
sms_result = await get_sms_code_services(request, query_db, user)
if sms_result.is_success:
logger.info('获取成功')
return response_200(data=sms_result, message='获取成功')
user_register_result = await LoginService.register_user_services(request, query_db, user_register)
if user_register_result.is_success:
logger.info(user_register_result.message)
return ResponseUtil.success(data=user_register_result, msg=user_register_result.message)
else:
logger.warning(sms_result.message)
return response_400(data='', message=sms_result.message)
logger.warning(user_register_result.message)
return ResponseUtil.failure(msg=user_register_result.message)
except Exception as e:
logger.exception(e)
return response_500(data="", message=str(e))
return ResponseUtil.error(msg=str(e))
@loginController.post("/forgetPwd", response_model=CrudResponseModel)
async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)):
try:
forget_user_result = await forget_user_services(request, query_db, forget_user)
if forget_user_result.is_success:
logger.info(forget_user_result.message)
return response_200(data=forget_user_result, message=forget_user_result.message)
else:
logger.warning(forget_user_result.message)
return response_400(data="", message=forget_user_result.message)
except Exception as e:
logger.exception(e)
return response_500(data="", message=str(e))
# @loginController.post("/getSmsCode", response_model=SmsCode)
# async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)):
# try:
# sms_result = await LoginService.get_sms_code_services(request, query_db, user)
# if sms_result.is_success:
# logger.info('获取成功')
# return ResponseUtil.success(data=sms_result)
# else:
# logger.warning(sms_result.message)
# return ResponseUtil.failure(msg=sms_result.message)
# except Exception as e:
# logger.exception(e)
# return ResponseUtil.error(msg=str(e))
#
#
# @loginController.post("/forgetPwd", response_model=CrudResponseModel)
# async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)):
# try:
# forget_user_result = await LoginService.forget_user_services(request, query_db, forget_user)
# if forget_user_result.is_success:
# logger.info(forget_user_result.message)
# return ResponseUtil.success(data=forget_user_result, msg=forget_user_result.message)
# else:
# logger.warning(forget_user_result.message)
# return ResponseUtil.failure(msg=forget_user_result.message)
# except Exception as e:
# logger.exception(e)
# return ResponseUtil.error(msg=str(e))
@loginController.post("/logout")
async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
session_id: str = payload.get("session_id")
await logout_services(request, session_id)
await LoginService.logout_services(request, session_id)
logger.info('退出成功')
return ResponseUtil.success(msg="退出成功")
except Exception as e:

7
ruoyi-fastapi-backend/module_admin/controller/notice_controller.py

@ -16,11 +16,8 @@ noticeController = APIRouter(prefix='/system/notice', dependencies=[Depends(Logi
@noticeController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:list'))])
async def get_system_notice_list(request: Request, notice_page_query: NoticePageQueryModel = Depends(NoticePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
notice_query = NoticeQueryModel(**notice_page_query.model_dump(by_alias=True))
# 获取全量数据
notice_query_result = NoticeService.get_notice_list_services(query_db, notice_query)
# 分页操作
notice_page_query_result = get_page_obj(notice_query_result, notice_page_query.page_num, notice_page_query.page_size)
# 获取分页数据
notice_page_query_result = NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=notice_page_query_result)
except Exception as e:

10
ruoyi-fastapi-backend/module_admin/controller/post_controler.py

@ -18,11 +18,8 @@ postController = APIRouter(prefix='/system/post', dependencies=[Depends(LoginSer
@postController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:list'))])
async def get_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
post_query = PostModel(**post_page_query.model_dump(by_alias=True))
# 获取全量数据
post_query_result = PostService.get_post_list_services(query_db, post_query)
# 分页操作
post_page_query_result = get_page_obj(post_query_result, post_page_query.page_num, post_page_query.page_size)
# 获取分页数据
post_page_query_result = PostService.get_post_list_services(query_db, post_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=post_page_query_result)
except Exception as e:
@ -98,9 +95,8 @@ async def query_detail_system_post(request: Request, post_id: int, query_db: Ses
@log_decorator(title='岗位管理', business_type=5)
async def export_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
post_query = PostModel(**post_page_query.model_dump(by_alias=True))
# 获取全量数据
post_query_result = PostService.get_post_list_services(query_db, post_query)
post_query_result = PostService.get_post_list_services(query_db, post_page_query, is_page=False)
post_export_result = PostService.export_post_list_services(post_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(post_export_result))

20
ruoyi-fastapi-backend/module_admin/controller/role_controller.py

@ -7,7 +7,7 @@ from module_admin.service.dept_service import DeptService, DeptModel
from module_admin.service.user_service import UserService, UserRoleQueryModel, UserRolePageQueryModel, CrudUserRoleModel
from utils.response_util import *
from utils.log_util import *
from utils.page_util import *
from utils.page_util import PageResponseModel
from utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.aspect.data_scope import GetDataScope
@ -33,10 +33,7 @@ async def get_system_role_dept_tree(request: Request, role_id: int, query_db: Se
@roleController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))])
async def get_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True))
role_query_result = RoleService.get_role_list_services(query_db, role_query)
# 分页操作
role_page_query_result = get_page_obj(role_query_result, role_page_query.page_num, role_page_query.page_size)
role_page_query_result = RoleService.get_role_list_services(query_db, role_page_query, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=role_page_query_result)
except Exception as e:
@ -134,9 +131,8 @@ async def query_detail_system_role(request: Request, role_id: int, query_db: Ses
@log_decorator(title='角色管理', business_type=5)
async def export_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_form), query_db: Session = Depends(get_db)):
try:
role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True))
# 获取全量数据
role_query_result = RoleService.get_role_list_services(query_db, role_query)
role_query_result = RoleService.get_role_list_services(query_db, role_page_query, is_page=False)
role_export_result = RoleService.export_role_list_services(role_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(role_export_result))
@ -167,10 +163,7 @@ async def reset_system_role_status(request: Request, edit_role: AddRoleModel, qu
@roleController.get("/authUser/allocatedList", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_allocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True))
role_user_allocated_query_result = RoleService.get_role_user_allocated_list_services(query_db, role_user_query)
# 分页操作
role_user_allocated_page_query_result = get_page_obj(role_user_allocated_query_result, user_role.page_num, user_role.page_size)
role_user_allocated_page_query_result = RoleService.get_role_user_allocated_list_services(query_db, user_role, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=role_user_allocated_page_query_result)
except Exception as e:
@ -181,10 +174,7 @@ async def get_system_allocated_user_list(request: Request, user_role: UserRolePa
@roleController.get("/authUser/unallocatedList", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
async def get_system_unallocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try:
role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True))
role_user_unallocated_query_result = RoleService.get_role_user_unallocated_list_services(query_db, role_user_query)
# 分页操作
role_user_unallocated_page_query_result = get_page_obj(role_user_unallocated_query_result, user_role.page_num, user_role.page_size)
role_user_unallocated_page_query_result = RoleService.get_role_user_unallocated_list_services(query_db, user_role, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=role_user_unallocated_page_query_result)
except Exception as e:

12
ruoyi-fastapi-backend/module_admin/controller/user_controller.py

@ -5,7 +5,7 @@ from config.env import UploadConfig
from module_admin.service.login_service import LoginService
from module_admin.service.user_service import *
from module_admin.service.dept_service import DeptService
from utils.page_util import *
from utils.page_util import PageResponseModel
from utils.response_util import *
from utils.log_util import *
from utils.common_util import bytes2file_response
@ -32,11 +32,8 @@ async def get_system_dept_tree(request: Request, query_db: Session = Depends(get
@userController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))])
async def get_system_user_list(request: Request, user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_query), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))):
try:
user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True))
# 获取全量数据
user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql)
# 分页操作
user_page_query_result = get_page_obj(user_query_result, user_page_query.page_num, user_page_query.page_size)
# 获取分页数据
user_page_query_result = UserService.get_user_list_services(query_db, user_page_query, data_scope_sql, is_page=True)
logger.info('获取成功')
return ResponseUtil.success(model_content=user_page_query_result)
except Exception as e:
@ -272,9 +269,8 @@ async def export_system_user_template(request: Request, query_db: Session = Depe
@log_decorator(title='用户管理', business_type=5)
async def export_system_user_list(request: Request, user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_form), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))):
try:
user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True))
# 获取全量数据
user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql)
user_query_result = UserService.get_user_list_services(query_db, user_page_query, data_scope_sql, is_page=False)
user_export_result = UserService.export_user_list_services(user_query_result)
logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(user_export_result))

9
ruoyi-fastapi-backend/module_admin/dao/config_dao.py

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session
from module_admin.entity.do.config_do import SysConfig
from module_admin.entity.vo.config_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time
@ -39,14 +40,15 @@ class ConfigDao:
return config_info
@classmethod
def get_config_list(cls, db: Session, query_object: ConfigQueryModel):
def get_config_list(cls, db: Session, query_object: ConfigPageQueryModel, is_page: bool = False):
"""
根据查询参数获取参数配置列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 参数配置列表信息对象
"""
config_list = db.query(SysConfig) \
query = db.query(SysConfig) \
.filter(SysConfig.config_name.like(f'%{query_object.config_name}%') if query_object.config_name else True,
SysConfig.config_key.like(f'%{query_object.config_key}%') if query_object.config_key else True,
SysConfig.config_type == query_object.config_type if query_object.config_type else True,
@ -55,7 +57,8 @@ class ConfigDao:
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
if query_object.begin_time and query_object.end_time else True
) \
.distinct().all()
.distinct()
config_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return config_list

17
ruoyi-fastapi-backend/module_admin/dao/dict_dao.py

@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
from module_admin.entity.do.dict_do import SysDictType, SysDictData
from module_admin.entity.vo.dict_vo import *
from utils.time_format_util import list_format_datetime
from utils.page_util import PageUtil
from datetime import datetime, time
@ -52,14 +53,15 @@ class DictTypeDao:
return list_format_datetime(dict_type_info)
@classmethod
def get_dict_type_list(cls, db: Session, query_object: DictTypeQueryModel):
def get_dict_type_list(cls, db: Session, query_object: DictTypePageQueryModel, is_page: bool = False):
"""
根据查询参数获取字典类型列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典类型列表信息对象
"""
dict_type_list = db.query(SysDictType) \
query = db.query(SysDictType) \
.filter(SysDictType.dict_name.like(f'%{query_object.dict_name}%') if query_object.dict_name else True,
SysDictType.dict_type.like(f'%{query_object.dict_type}%') if query_object.dict_type else True,
SysDictType.status == query_object.status if query_object.status else True,
@ -68,7 +70,8 @@ class DictTypeDao:
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
if query_object.begin_time and query_object.end_time else True
) \
.distinct().all()
.distinct()
dict_type_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return dict_type_list
@ -147,20 +150,22 @@ class DictDataDao:
return dict_data_info
@classmethod
def get_dict_data_list(cls, db: Session, query_object: DictDataModel):
def get_dict_data_list(cls, db: Session, query_object: DictDataPageQueryModel, is_page: bool = False):
"""
根据查询参数获取字典数据列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典数据列表信息对象
"""
dict_data_list = db.query(SysDictData) \
query = db.query(SysDictData) \
.filter(SysDictData.dict_type == query_object.dict_type if query_object.dict_type else True,
SysDictData.dict_label.like(f'%{query_object.dict_label}%') if query_object.dict_label else True,
SysDictData.status == query_object.status if query_object.status else True
) \
.order_by(SysDictData.dict_sort) \
.distinct().all()
.distinct()
dict_data_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return dict_data_list

9
ruoyi-fastapi-backend/module_admin/dao/job_dao.py

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session
from module_admin.entity.do.job_do import SysJob
from module_admin.entity.vo.job_vo import *
from utils.page_util import PageUtil
class JobDao:
@ -40,19 +41,21 @@ class JobDao:
return job_info
@classmethod
def get_job_list(cls, db: Session, query_object: JobModel):
def get_job_list(cls, db: Session, query_object: JobPageQueryModel, is_page: bool = False):
"""
根据查询参数获取定时任务列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务列表信息对象
"""
job_list = db.query(SysJob) \
query = db.query(SysJob) \
.filter(SysJob.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
SysJob.job_group == query_object.job_group if query_object.job_group else True,
SysJob.status == query_object.status if query_object.status else True
) \
.distinct().all()
.distinct()
job_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return job_list

9
ruoyi-fastapi-backend/module_admin/dao/job_log_dao.py

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session
from module_admin.entity.do.job_do import SysJobLog
from module_admin.entity.vo.job_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time
@ -10,14 +11,15 @@ class JobLogDao:
"""
@classmethod
def get_job_log_list(cls, db: Session, query_object: JobLogQueryModel):
def get_job_log_list(cls, db: Session, query_object: JobLogPageQueryModel, is_page: bool = False):
"""
根据查询参数获取定时任务日志列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务日志列表信息对象
"""
job_log_list = db.query(SysJobLog) \
query = db.query(SysJobLog) \
.filter(SysJobLog.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
SysJobLog.job_group == query_object.job_group if query_object.job_group else True,
SysJobLog.status == query_object.status if query_object.status else True,
@ -26,7 +28,8 @@ class JobLogDao:
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
if query_object.begin_time and query_object.end_time else True
) \
.distinct().all()
.distinct()
job_log_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return job_log_list

18
ruoyi-fastapi-backend/module_admin/dao/log_dao.py

@ -1,7 +1,7 @@
from sqlalchemy.orm import Session
from module_admin.entity.do.log_do import SysOperLog, SysLogininfor
from module_admin.entity.vo.log_vo import *
from utils.time_format_util import object_format_datetime, list_format_datetime
from utils.page_util import PageUtil
from datetime import datetime, time
@ -10,14 +10,15 @@ class OperationLogDao:
操作日志管理模块数据库操作层
"""
@classmethod
def get_operation_log_list(cls, db: Session, query_object: OperLogQueryModel):
def get_operation_log_list(cls, db: Session, query_object: OperLogPageQueryModel, is_page: bool = False):
"""
根据查询参数获取操作日志列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 操作日志列表信息对象
"""
operation_log_list = db.query(SysOperLog) \
query = db.query(SysOperLog) \
.filter(SysOperLog.title.like(f'%{query_object.title}%') if query_object.title else True,
SysOperLog.oper_name.like(f'%{query_object.oper_name}%') if query_object.oper_name else True,
SysOperLog.business_type == query_object.business_type if query_object.business_type else True,
@ -27,7 +28,8 @@ class OperationLogDao:
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
if query_object.begin_time and query_object.end_time else True
)\
.distinct().all()
.distinct()
operation_log_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return operation_log_list
@ -74,14 +76,15 @@ class LoginLogDao:
"""
@classmethod
def get_login_log_list(cls, db: Session, query_object: LoginLogQueryModel):
def get_login_log_list(cls, db: Session, query_object: LoginLogPageQueryModel, is_page: bool = False):
"""
根据查询参数获取登录日志列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 登录日志列表信息对象
"""
login_log_list = db.query(SysLogininfor) \
query = db.query(SysLogininfor) \
.filter(SysLogininfor.ipaddr.like(f'%{query_object.ipaddr}%') if query_object.ipaddr else True,
SysLogininfor.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
SysLogininfor.status == query_object.status if query_object.status else True,
@ -90,7 +93,8 @@ class LoginLogDao:
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
if query_object.begin_time and query_object.end_time else True
)\
.distinct().all()
.distinct()
login_log_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return login_log_list

9
ruoyi-fastapi-backend/module_admin/dao/notice_dao.py

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session
from module_admin.entity.do.notice_do import SysNotice
from module_admin.entity.vo.notice_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time
@ -40,14 +41,15 @@ class NoticeDao:
return notice_info
@classmethod
def get_notice_list(cls, db: Session, query_object: NoticeQueryModel):
def get_notice_list(cls, db: Session, query_object: NoticePageQueryModel, is_page: bool = False):
"""
根据查询参数获取通知公告列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 通知公告列表信息对象
"""
notice_list = db.query(SysNotice) \
query = db.query(SysNotice) \
.filter(SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True,
SysNotice.update_by.like(f'%{query_object.update_by}%') if query_object.update_by else True,
SysNotice.notice_type == query_object.notice_type if query_object.notice_type else True,
@ -56,7 +58,8 @@ class NoticeDao:
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
if query_object.begin_time and query_object.end_time else True
) \
.distinct().all()
.distinct()
notice_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return notice_list

9
ruoyi-fastapi-backend/module_admin/dao/post_dao.py

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session
from module_admin.entity.do.post_do import SysPost
from module_admin.entity.vo.post_vo import *
from utils.page_util import PageUtil
class PostDao:
@ -54,20 +55,22 @@ class PostDao:
return post_info
@classmethod
def get_post_list(cls, db: Session, query_object: PostModel):
def get_post_list(cls, db: Session, query_object: PostPageQueryModel, is_page: bool = False):
"""
根据查询参数获取岗位列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 岗位列表信息对象
"""
post_list = db.query(SysPost) \
query = db.query(SysPost) \
.filter(SysPost.post_code.like(f'%{query_object.post_code}%') if query_object.post_code else True,
SysPost.post_name.like(f'%{query_object.post_name}%') if query_object.post_name else True,
SysPost.status == query_object.status if query_object.status else True
) \
.order_by(SysPost.post_sort) \
.distinct().all()
.distinct()
post_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return post_list

9
ruoyi-fastapi-backend/module_admin/dao/role_dao.py

@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.vo.role_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time
@ -85,14 +86,15 @@ class RoleDao:
return role_info
@classmethod
def get_role_list(cls, db: Session, query_object: RoleQueryModel):
def get_role_list(cls, db: Session, query_object: RolePageQueryModel, is_page: bool = False):
"""
根据查询参数获取角色列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 角色列表信息对象
"""
role_list = db.query(SysRole) \
query = db.query(SysRole) \
.filter(SysRole.del_flag == 0,
SysRole.role_name.like(f'%{query_object.role_name}%') if query_object.role_name else True,
SysRole.role_key.like(f'%{query_object.role_key}%') if query_object.role_key else True,
@ -103,7 +105,8 @@ class RoleDao:
if query_object.begin_time and query_object.end_time else True
) \
.order_by(SysRole.role_sort) \
.distinct().all()
.distinct()
role_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return role_list

47
ruoyi-fastapi-backend/module_admin/dao/user_dao.py

@ -6,7 +6,7 @@ from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.do.post_do import SysPost
from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.vo.user_vo import *
from utils.time_format_util import list_format_datetime
from utils.page_util import PageUtil
from datetime import datetime, time
@ -137,15 +137,16 @@ class UserDao:
return results
@classmethod
def get_user_list(cls, db: Session, query_object: UserQueryModel, data_scope_sql: str):
def get_user_list(cls, db: Session, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False):
"""
根据查询参数获取用户列表信息
:param db: orm对象
:param query_object: 查询参数对象
:param data_scope_sql: 数据权限对应的查询sql语句
:param is_page: 是否开启分页
:return: 用户列表信息对象
"""
user_list = db.query(SysUser, SysDept) \
query = db.query(SysUser, SysDept) \
.filter(SysUser.del_flag == 0,
or_(SysUser.dept_id == query_object.dept_id, SysUser.dept_id.in_(
db.query(SysDept.dept_id).filter(func.find_in_set(query_object.dept_id, SysDept.ancestors))
@ -163,7 +164,8 @@ class UserDao:
eval(data_scope_sql)
) \
.outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
.distinct().all()
.distinct()
user_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return user_list
@ -227,35 +229,15 @@ class UserDao:
return allocated_role_list
@classmethod
def get_user_role_unallocated_list_by_user_id(cls, db: Session, query_object: UserRoleQueryModel):
"""
根据用户id获取用户未分配的角色列表信息数据库操作
:param db: orm对象
:param query_object: 用户角色查询对象
:return: 用户未分配的角色列表信息
"""
unallocated_role_list = db.query(SysRole) \
.filter(
SysRole.del_flag == 0,
SysRole.role_id != 1,
SysRole.role_name == query_object.role_name if query_object.role_name else True,
SysRole.role_key == query_object.role_key if query_object.role_key else True,
~SysRole.role_id.in_(
db.query(SysUserRole.role_id).filter(SysUserRole.user_id == query_object.user_id)
)
).distinct().all()
return list_format_datetime(unallocated_role_list)
@classmethod
def get_user_role_allocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel):
def get_user_role_allocated_list_by_role_id(cls, db: Session, query_object: UserRolePageQueryModel, is_page: bool = False):
"""
根据角色id获取已分配的用户列表信息
:param db: orm对象
:param query_object: 用户角色查询对象
:param is_page: 是否开启分页
:return: 角色已分配的用户列表信息
"""
allocated_user_list = db.query(SysUser) \
query = db.query(SysUser) \
.filter(
SysUser.del_flag == 0,
SysUser.user_id != 1,
@ -264,19 +246,21 @@ class UserDao:
SysUser.user_id.in_(
db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id)
)
).distinct().all()
).distinct()
allocated_user_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return allocated_user_list
@classmethod
def get_user_role_unallocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel):
def get_user_role_unallocated_list_by_role_id(cls, db: Session, query_object: UserRolePageQueryModel, is_page: bool = False):
"""
根据角色id获取未分配的用户列表信息
:param db: orm对象
:param query_object: 用户角色查询对象
:param is_page: 是否开启分页
:return: 角色未分配的用户列表信息
"""
unallocated_user_list = db.query(SysUser) \
query = db.query(SysUser) \
.filter(
SysUser.del_flag == 0,
SysUser.user_id != 1,
@ -285,7 +269,8 @@ class UserDao:
~SysUser.user_id.in_(
db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id)
)
).distinct().all()
).distinct()
unallocated_user_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
return unallocated_user_list

4
ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py

@ -37,8 +37,8 @@ class ConfigPageQueryModel(ConfigQueryModel):
"""
参数配置管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteConfigModel(BaseModel):

10
ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py

@ -58,8 +58,8 @@ class DictTypePageQueryModel(DictTypeQueryModel):
"""
字典类型管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteDictTypeModel(BaseModel):
@ -71,7 +71,7 @@ class DeleteDictTypeModel(BaseModel):
dict_ids: str
class DictDataQueryModel(DictTypeModel):
class DictDataQueryModel(DictDataModel):
"""
字典数据管理不分页查询模型
"""
@ -85,8 +85,8 @@ class DictDataPageQueryModel(DictDataQueryModel):
"""
字典数据管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteDictDataModel(BaseModel):

8
ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py

@ -63,8 +63,8 @@ class JobPageQueryModel(JobQueryModel):
"""
定时任务管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class EditJobModel(JobModel):
@ -97,8 +97,8 @@ class JobLogPageQueryModel(JobLogQueryModel):
"""
定时任务日志管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteJobLogModel(BaseModel):

8
ruoyi-fastapi-backend/module_admin/entity/vo/log_vo.py

@ -61,8 +61,8 @@ class OperLogPageQueryModel(OperLogQueryModel):
"""
操作日志管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteOperLogModel(BaseModel):
@ -89,8 +89,8 @@ class LoginLogPageQueryModel(LoginLogQueryModel):
"""
登录日志管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteLoginLogModel(BaseModel):

11
ruoyi-fastapi-backend/module_admin/entity/vo/login_vo.py

@ -14,6 +14,16 @@ class UserLogin(BaseModel):
captcha_enabled: Optional[bool] = None
class UserRegister(BaseModel):
model_config = ConfigDict(alias_generator=to_camel)
username: str
password: str
confirm_password: str
code: Optional[str] = None
uuid: Optional[str] = None
class Token(BaseModel):
access_token: str
token_type: str
@ -23,6 +33,7 @@ class CaptchaCode(BaseModel):
model_config = ConfigDict(alias_generator=to_camel)
captcha_enabled: bool
register_enabled: bool
img: str
uuid: str

4
ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py

@ -37,8 +37,8 @@ class NoticePageQueryModel(NoticeQueryModel):
"""
通知公告管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeleteNoticeModel(BaseModel):

4
ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py

@ -37,8 +37,8 @@ class PostPageQueryModel(PostQueryModel):
"""
岗位管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class DeletePostModel(BaseModel):

4
ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py

@ -83,8 +83,8 @@ class RolePageQueryModel(RoleQueryModel):
"""
角色管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class RoleMenuQueryModel(BaseModel):

8
ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py

@ -124,8 +124,8 @@ class UserPageQueryModel(UserQueryModel):
"""
用户管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class AddUserModel(UserModel):
@ -176,8 +176,8 @@ class UserRolePageQueryModel(UserRoleQueryModel):
"""
用户角色关联管理分页查询模型
"""
page_num: int
page_size: int
page_num: int = 1
page_size: int = 10
class SelectedRoleModel(RoleModel):

13
ruoyi-fastapi-backend/module_admin/service/config_service.py

@ -11,16 +11,17 @@ class ConfigService:
"""
@classmethod
def get_config_list_services(cls, query_db: Session, query_object: ConfigQueryModel):
def get_config_list_services(cls, query_db: Session, query_object: ConfigPageQueryModel, is_page: bool = False):
"""
获取参数配置列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 参数配置列表信息对象
"""
config_list_result = ConfigDao.get_config_list(query_db, query_object)
config_list_result = ConfigDao.get_config_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(config_list_result)
return config_list_result
@classmethod
async def init_cache_sys_config_services(cls, query_db: Session, redis):
@ -35,10 +36,10 @@ class ConfigService:
# 删除匹配的键
if keys:
await redis.delete(*keys)
config_all = ConfigDao.get_config_list(query_db, ConfigQueryModel(**dict()))
config_all = ConfigDao.get_config_list(query_db, ConfigPageQueryModel(**dict()), is_page=False)
for config_obj in config_all:
if config_obj.config_type == 'Y':
await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.config_key}", config_obj.config_value)
if config_obj.get('configType') == 'Y':
await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.get('configKey')}", config_obj.get('configValue'))
@classmethod
async def query_config_list_from_cache_services(cls, redis, config_key: str):

14
ruoyi-fastapi-backend/module_admin/service/dict_service.py

@ -12,16 +12,17 @@ class DictTypeService:
"""
@classmethod
def get_dict_type_list_services(cls, query_db: Session, query_object: DictTypeQueryModel):
def get_dict_type_list_services(cls, query_db: Session, query_object: DictTypePageQueryModel, is_page: bool = False):
"""
获取字典类型列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典类型列表信息对象
"""
dict_type_list_result = DictTypeDao.get_dict_type_list(query_db, query_object)
dict_type_list_result = DictTypeDao.get_dict_type_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(dict_type_list_result)
return dict_type_list_result
@classmethod
async def add_dict_type_services(cls, request: Request, query_db: Session, page_object: DictTypeModel):
@ -172,16 +173,17 @@ class DictDataService:
"""
@classmethod
def get_dict_data_list_services(cls, query_db: Session, query_object: DictDataModel):
def get_dict_data_list_services(cls, query_db: Session, query_object: DictDataPageQueryModel, is_page: bool = False):
"""
获取字典数据列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典数据列表信息对象
"""
dict_data_list_result = DictDataDao.get_dict_data_list(query_db, query_object)
dict_data_list_result = DictDataDao.get_dict_data_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(dict_data_list_result)
return dict_data_list_result
@classmethod
def query_dict_data_list_services(cls, query_db: Session, dict_type: str):

28
ruoyi-fastapi-backend/module_admin/service/job_log_service.py

@ -1,7 +1,7 @@
from module_admin.dao.job_log_dao import *
from module_admin.dao.dict_dao import DictDataDao
from module_admin.service.dict_service import Request, DictDataService
from module_admin.entity.vo.common_vo import CrudResponseModel
from utils.common_util import export_list2excel, CamelCaseUtil
from utils.common_util import export_list2excel
class JobLogService:
@ -10,16 +10,17 @@ class JobLogService:
"""
@classmethod
def get_job_log_list_services(cls, query_db: Session, query_object: JobLogQueryModel):
def get_job_log_list_services(cls, query_db: Session, query_object: JobLogPageQueryModel, is_page: bool = False):
"""
获取定时任务日志列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务日志列表信息对象
"""
job_log_list_result = JobLogDao.get_job_log_list(query_db, query_object)
job_log_list_result = JobLogDao.get_job_log_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(job_log_list_result)
return job_log_list_result
@classmethod
def add_job_log_services(cls, query_db: Session, page_object: JobLogModel):
@ -79,10 +80,10 @@ class JobLogService:
return CrudResponseModel(**result)
@staticmethod
def export_job_log_list_services(query_db, job_log_list: List):
async def export_job_log_list_services(request: Request, job_log_list: List):
"""
导出定时任务日志信息service
:param query_db: orm对象
:param request: Request对象
:param job_log_list: 定时任务日志信息列表
:return: 定时任务日志信息对应excel的二进制数据
"""
@ -103,17 +104,22 @@ class JobLogService:
}
data = job_log_list
job_group_list = DictDataDao.query_dict_data_list(query_db, dict_type='sys_job_group')
job_group_option = [dict(label=item.dict_label, value=item.dict_value) for item in job_group_list]
job_group_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_job_group')
job_group_option = [dict(label=item.get('dictLabel'), value=item.get('dictValue')) for item in job_group_list]
job_group_option_dict = {item.get('value'): item for item in job_group_option}
job_executor_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_job_executor')
job_executor_option = [dict(label=item.get('dictLabel'), value=item.get('dictValue')) for item in job_executor_list]
job_executor_option_dict = {item.get('value'): item for item in job_executor_option}
for item in data:
if item.get('status') == '0':
item['status'] = '正常'
else:
item['status'] = '暂停'
if str(item.get('job_group')) in job_group_option_dict.keys():
item['job_group'] = job_group_option_dict.get(str(item.get('job_group'))).get('label')
if str(item.get('jobGroup')) in job_group_option_dict.keys():
item['jobGroup'] = job_group_option_dict.get(str(item.get('jobGroup'))).get('label')
if str(item.get('jobExecutor')) in job_executor_option_dict.keys():
item['jobExecutor'] = job_executor_option_dict.get(str(item.get('jobExecutor'))).get('label')
new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in
data]
binary_data = export_list2excel(new_data)

7
ruoyi-fastapi-backend/module_admin/service/job_service.py

@ -11,16 +11,17 @@ class JobService:
"""
@classmethod
def get_job_list_services(cls, query_db: Session, query_object: JobModel):
def get_job_list_services(cls, query_db: Session, query_object: JobPageQueryModel, is_page: bool = False):
"""
获取定时任务列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务列表信息对象
"""
job_list_result = JobDao.get_job_list(query_db, query_object)
job_list_result = JobDao.get_job_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(job_list_result)
return job_list_result
@classmethod
def add_job_services(cls, query_db: Session, page_object: JobModel):

14
ruoyi-fastapi-backend/module_admin/service/log_service.py

@ -10,16 +10,17 @@ class OperationLogService:
"""
@classmethod
def get_operation_log_list_services(cls, query_db: Session, query_object: OperLogQueryModel):
def get_operation_log_list_services(cls, query_db: Session, query_object: OperLogPageQueryModel, is_page: bool = False):
"""
获取操作日志列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 操作日志列表信息对象
"""
operation_log_list_result = OperationLogDao.get_operation_log_list(query_db, query_object)
operation_log_list_result = OperationLogDao.get_operation_log_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(operation_log_list_result)
return operation_log_list_result
@classmethod
def add_operation_log_services(cls, query_db: Session, page_object: OperLogModel):
@ -131,16 +132,17 @@ class LoginLogService:
"""
@classmethod
def get_login_log_list_services(cls, query_db: Session, query_object: LoginLogQueryModel):
def get_login_log_list_services(cls, query_db: Session, query_object: LoginLogPageQueryModel, is_page: bool = False):
"""
获取登录日志列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 登录日志列表信息对象
"""
operation_log_list_result = LoginLogDao.get_login_log_list(query_db, query_object)
operation_log_list_result = LoginLogDao.get_login_log_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(operation_log_list_result)
return operation_log_list_result
@classmethod
def add_login_log_services(cls, query_db: Session, page_object: LogininforModel):

81
ruoyi-fastapi-backend/module_admin/service/login_service.py

@ -9,12 +9,13 @@ from module_admin.service.user_service import *
from module_admin.entity.vo.login_vo import *
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.dao.login_dao import *
from config.env import JwtConfig, RedisInitKeyConfig
from exceptions.exception import LoginException, AuthException
from config.env import AppConfig, JwtConfig, RedisInitKeyConfig
from config.get_db import get_db
from utils.common_util import CamelCaseUtil
from utils.pwd_util import *
from utils.response_util import *
from utils.message_util import *
from config.get_db import get_db
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
@ -60,8 +61,13 @@ class LoginService:
if login_user.user_name == account_lock:
logger.warning("账号已锁定,请稍后再试")
raise LoginException(data="", message="账号已锁定,请稍后再试")
# 判断是否开启验证码,开启则验证,否则不验证
if login_user.captcha_enabled:
# 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
# 判断是否开启验证码,开启则验证,否则不验证(dev模式下来自API文档的登录请求不检验)
if not login_user.captcha_enabled or ((request_from_swagger or request_from_redoc) and AppConfig.app_env == 'dev'):
pass
else:
await cls.__check_login_captcha(request, login_user)
user = login_by_account(query_db, login_user.user_name)
if not user:
@ -124,9 +130,9 @@ class LoginService:
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
expire = datetime.utcnow() + timedelta(minutes=30)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, JwtConfig.SECRET_KEY, algorithm=JwtConfig.ALGORITHM)
encoded_jwt = jwt.encode(to_encode, JwtConfig.jwt_secret_key, algorithm=JwtConfig.jwt_algorithm)
return encoded_jwt
@classmethod
@ -146,7 +152,7 @@ class LoginService:
try:
if token.startswith('Bearer'):
token = token.split(' ')[1]
payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
user_id: str = payload.get("user_id")
session_id: str = payload.get("session_id")
if user_id is None:
@ -165,9 +171,9 @@ class LoginService:
# redis_token = await request.app.state.redis.get(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}")
if token == redis_token:
await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", redis_token,
ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
# await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}", redis_token,
# ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
# ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
role_id_list = [item.role_id for item in query_user.get('user_role_info')]
if 1 in role_id_list:
@ -255,8 +261,46 @@ class LoginService:
return router_list
@classmethod
async def register_user_services(cls, request: Request, query_db: Session, user_register: UserRegister):
"""
用户注册services
:param request: Request对象
:param query_db: orm对象
:param user_register: 注册用户对象
:return: 注册结果
"""
register_enabled = True if await request.app.state.redis.get(
f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.registerUser") == 'true' else False
captcha_enabled = True if await request.app.state.redis.get(
f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == 'true' else False
if user_register.password == user_register.confirm_password:
if register_enabled:
if captcha_enabled:
captcha_value = await request.app.state.redis.get(
f"{RedisInitKeyConfig.CAPTCHA_CODES.get('key')}:{user_register.uuid}")
if not captcha_value:
logger.warning("验证码已失效")
return CrudResponseModel(is_success=False, message='验证码已失效')
elif user_register.code != str(captcha_value):
logger.warning("验证码错误")
return CrudResponseModel(is_success=False, message='验证码错误')
add_user = AddUserModel(
userName=user_register.username,
nickName=user_register.username,
password=PwdUtil.get_password_hash(user_register.password)
)
result = UserService.add_user_services(query_db, add_user)
return result
else:
result = dict(is_success=False, message='注册程序已关闭,禁止注册')
else:
result = dict(is_success=False, message='两次输入的密码不一致')
return CrudResponseModel(**result)
async def get_sms_code_services(request: Request, query_db: Session, user: ResetUserModel):
@classmethod
async def get_sms_code_services(cls, request: Request, query_db: Session, user: ResetUserModel):
"""
获取短信验证码service
:param request: Request对象
@ -264,14 +308,16 @@ async def get_sms_code_services(request: Request, query_db: Session, user: Reset
:param user: 用户对象
:return: 短信验证码对象
"""
redis_sms_result = await request.app.state.redis.get(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{user.session_id}")
redis_sms_result = await request.app.state.redis.get(
f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{user.session_id}")
if redis_sms_result:
return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='短信验证码仍在有效期内'))
is_user = UserDao.get_user_by_name(query_db, user.user_name)
if is_user:
sms_code = str(random.randint(100000, 999999))
session_id = str(uuid.uuid4())
await request.app.state.redis.set(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{session_id}", sms_code, ex=timedelta(minutes=2))
await request.app.state.redis.set(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{session_id}", sms_code,
ex=timedelta(minutes=2))
# 此处模拟调用短信服务
message_service(sms_code)
@ -279,8 +325,8 @@ async def get_sms_code_services(request: Request, query_db: Session, user: Reset
return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='用户不存在'))
async def forget_user_services(request: Request, query_db: Session, forget_user: ResetUserModel):
@classmethod
async def forget_user_services(cls, request: Request, query_db: Session, forget_user: ResetUserModel):
"""
用户忘记密码services
:param request: Request对象
@ -288,7 +334,8 @@ async def forget_user_services(request: Request, query_db: Session, forget_user:
:param forget_user: 重置用户对象
:return: 重置结果
"""
redis_sms_result = await request.app.state.redis.get(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{forget_user.session_id}")
redis_sms_result = await request.app.state.redis.get(
f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{forget_user.session_id}")
if forget_user.sms_code == redis_sms_result:
forget_user.password = PwdUtil.get_password_hash(forget_user.password)
forget_user.user_id = UserDao.get_user_by_name(query_db, forget_user.user_name).user_id
@ -302,8 +349,8 @@ async def forget_user_services(request: Request, query_db: Session, forget_user:
return CrudResponseModel(**result)
async def logout_services(request: Request, session_id: str):
@classmethod
async def logout_services(cls, request: Request, session_id: str):
"""
退出登录services
:param request: Request对象

7
ruoyi-fastapi-backend/module_admin/service/notice_service.py

@ -9,16 +9,17 @@ class NoticeService:
"""
@classmethod
def get_notice_list_services(cls, query_db: Session, query_object: NoticeQueryModel):
def get_notice_list_services(cls, query_db: Session, query_object: NoticePageQueryModel, is_page: bool = True):
"""
获取通知公告列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 通知公告列表信息对象
"""
notice_list_result = NoticeDao.get_notice_list(query_db, query_object)
notice_list_result = NoticeDao.get_notice_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(notice_list_result)
return notice_list_result
@classmethod
def add_notice_services(cls, query_db: Session, page_object: NoticeModel):

2
ruoyi-fastapi-backend/module_admin/service/online_service.py

@ -25,7 +25,7 @@ class OnlineService:
access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys]
online_info_list = []
for item in access_token_values_list:
payload = jwt.decode(item, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
payload = jwt.decode(item, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
online_dict = dict(
token_id=payload.get('session_id'),
user_name=payload.get('user_name'),

7
ruoyi-fastapi-backend/module_admin/service/post_service.py

@ -8,16 +8,17 @@ class PostService:
岗位管理模块服务层
"""
@classmethod
def get_post_list_services(cls, query_db: Session, query_object: PostModel):
def get_post_list_services(cls, query_db: Session, query_object: PostPageQueryModel, is_page: bool = False):
"""
获取岗位列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 岗位列表信息对象
"""
post_list_result = PostDao.get_post_list(query_db, query_object)
post_list_result = PostDao.get_post_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(post_list_result)
return post_list_result
@classmethod
def add_post_services(cls, query_db: Session, page_object: PostModel):

34
ruoyi-fastapi-backend/module_admin/service/role_service.py

@ -1,7 +1,8 @@
from module_admin.entity.vo.user_vo import UserInfoModel, UserRoleQueryModel
from module_admin.entity.vo.user_vo import UserInfoModel, UserRolePageQueryModel
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.dao.user_dao import UserDao
from module_admin.dao.role_dao import *
from utils.page_util import PageResponseModel
from utils.common_util import export_list2excel, CamelCaseUtil
@ -38,16 +39,17 @@ class RoleService:
return result
@classmethod
def get_role_list_services(cls, query_db: Session, query_object: RoleQueryModel):
def get_role_list_services(cls, query_db: Session, query_object: RolePageQueryModel, is_page: bool = False):
"""
获取角色列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 角色列表信息对象
"""
role_list_result = RoleDao.get_role_list(query_db, query_object)
role_list_result = RoleDao.get_role_list(query_db, query_object, is_page)
return CamelCaseUtil.transform_result(role_list_result)
return role_list_result
@classmethod
def add_role_services(cls, query_db: Session, page_object: AddRoleModel):
@ -230,27 +232,39 @@ class RoleService:
return binary_data
@classmethod
def get_role_user_allocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel):
def get_role_user_allocated_list_services(cls, query_db: Session, page_object: UserRolePageQueryModel, is_page: bool = False):
"""
根据角色id获取已分配用户列表
:param query_db: orm对象
:param page_object: 用户关联角色对象
:param is_page: 是否开启分页
:return: 已分配用户列表
"""
query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object)
allocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list]
query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object, is_page)
allocated_list = PageResponseModel(
**{
**query_user_list.model_dump(by_alias=True),
'rows': [UserInfoModel(**row) for row in query_user_list.rows]
}
)
return allocated_list
@classmethod
def get_role_user_unallocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel):
def get_role_user_unallocated_list_services(cls, query_db: Session, page_object: UserRolePageQueryModel, is_page: bool = False):
"""
根据角色id获取未分配用户列表
:param query_db: orm对象
:param page_object: 用户关联角色对象
:param is_page: 是否开启分页
:return: 未分配用户列表
"""
query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object)
unallocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list]
query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object, is_page)
unallocated_list = PageResponseModel(
**{
**query_user_list.model_dump(by_alias=True),
'rows': [UserInfoModel(**row) for row in query_user_list.rows]
}
)
return unallocated_list

20
ruoyi-fastapi-backend/module_admin/service/user_service.py

@ -1,8 +1,9 @@
from fastapi import UploadFile
from module_admin.service.role_service import RoleService
from module_admin.service.post_service import PostService
from module_admin.service.post_service import PostService, PostPageQueryModel
from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.dao.user_dao import *
from utils.page_util import PageResponseModel
from utils.pwd_util import *
from utils.common_util import *
@ -13,18 +14,27 @@ class UserService:
"""
@classmethod
def get_user_list_services(cls, query_db: Session, query_object: UserQueryModel, data_scope_sql: str):
def get_user_list_services(cls, query_db: Session, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False):
"""
获取用户列表信息service
:param query_db: orm对象
:param query_object: 查询参数对象
:param data_scope_sql: 数据权限对应的查询sql语句
:param is_page: 是否开启分页
:return: 用户列表信息对象
"""
query_result = UserDao.get_user_list(query_db, query_object, data_scope_sql)
query_result = UserDao.get_user_list(query_db, query_object, data_scope_sql, is_page)
if is_page:
user_list_result = PageResponseModel(
**{
**query_result.model_dump(by_alias=True),
'rows': [{**row[0], 'dept': row[1]} for row in query_result.rows]
}
)
else:
user_list_result = []
if query_result:
user_list_result = [{**CamelCaseUtil.transform_result(row[0]), 'dept': CamelCaseUtil.transform_result(row[1])} for row in query_result]
user_list_result = [{**row[0], 'dept': row[1]} for row in query_result]
return user_list_result
@ -134,7 +144,7 @@ class UserService:
:param user_id: 用户id
:return: 用户id对应的信息
"""
posts = PostService.get_post_list_services(query_db, PostModel(**{}))
posts = PostService.get_post_list_services(query_db, PostPageQueryModel(**{}), is_page=False)
roles = RoleService.get_role_select_option_services(query_db)
if user_id != '':
query_user = UserDao.get_user_detail_by_id(query_db, user_id=user_id)

15
ruoyi-fastapi-backend/requirements.txt

@ -0,0 +1,15 @@
APScheduler==3.10.4
DateTime==5.4
fastapi[all]==0.109.0
loguru==0.7.2
openpyxl==3.1.2
pandas==2.1.4
passlib[bcrypt]==1.7.4
Pillow==10.2.0
psutil==5.9.7
PyMySQL==1.1.0
python-jose[cryptography]==3.3.0
redis==5.0.1
requests==2.31.0
SQLAlchemy==2.0.25
user-agents==2.2.0

83
ruoyi-fastapi-backend/server.py

@ -0,0 +1,83 @@
from fastapi import FastAPI
from contextlib import asynccontextmanager
from sub_applications.handle import handle_sub_applications
from middlewares.handle import handle_middleware
from exceptions.handle import handle_exception
from module_admin.controller.login_controller import loginController
from module_admin.controller.captcha_controller import captchaController
from module_admin.controller.user_controller import userController
from module_admin.controller.menu_controller import menuController
from module_admin.controller.dept_controller import deptController
from module_admin.controller.role_controller import roleController
from module_admin.controller.post_controler import postController
from module_admin.controller.dict_controller import dictController
from module_admin.controller.config_controller import configController
from module_admin.controller.notice_controller import noticeController
from module_admin.controller.log_controller import logController
from module_admin.controller.online_controller import onlineController
from module_admin.controller.job_controller import jobController
from module_admin.controller.server_controller import serverController
from module_admin.controller.cache_controller import cacheController
from module_admin.controller.common_controller import commonController
from config.env import AppConfig
from config.get_redis import RedisUtil
from config.get_db import init_create_table
from config.get_scheduler import SchedulerUtil
from utils.log_util import logger
from utils.common_util import worship
# 生命周期事件
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info(f"{AppConfig.app_name}开始启动")
worship()
await init_create_table()
app.state.redis = await RedisUtil.create_redis_pool()
await RedisUtil.init_sys_dict(app.state.redis)
await RedisUtil.init_sys_config(app.state.redis)
await SchedulerUtil.init_system_scheduler()
logger.info(f"{AppConfig.app_name}启动成功")
yield
await RedisUtil.close_redis_pool(app)
await SchedulerUtil.close_system_scheduler()
# 初始化FastAPI对象
app = FastAPI(
title=AppConfig.app_name,
description=f'{AppConfig.app_name}接口文档',
version=AppConfig.app_version,
lifespan=lifespan
)
# 挂载子应用
handle_sub_applications(app)
# 加载中间件处理方法
handle_middleware(app)
# 加载全局异常处理方法
handle_exception(app)
# 加载路由列表
controller_list = [
{'router': loginController, 'tags': ['登录模块']},
{'router': captchaController, 'tags': ['验证码模块']},
{'router': userController, 'tags': ['系统管理-用户管理']},
{'router': roleController, 'tags': ['系统管理-角色管理']},
{'router': menuController, 'tags': ['系统管理-菜单管理']},
{'router': deptController, 'tags': ['系统管理-部门管理']},
{'router': postController, 'tags': ['系统管理-岗位管理']},
{'router': dictController, 'tags': ['系统管理-字典管理']},
{'router': configController, 'tags': ['系统管理-参数管理']},
{'router': noticeController, 'tags': ['系统管理-通知公告管理']},
{'router': logController, 'tags': ['系统管理-日志管理']},
{'router': onlineController, 'tags': ['系统监控-在线用户']},
{'router': jobController, 'tags': ['系统监控-定时任务']},
{'router': serverController, 'tags': ['系统监控-菜单管理']},
{'router': cacheController, 'tags': ['系统监控-缓存监控']},
{'router': commonController, 'tags': ['通用模块']}
]
for controller in controller_list:
app.include_router(router=controller.get('router'), tags=controller.get('tags'))

10
ruoyi-fastapi-backend/sub_applications/handle.py

@ -0,0 +1,10 @@
from fastapi import FastAPI
from sub_applications.staticfiles import mount_staticfiles
def handle_sub_applications(app: FastAPI):
"""
全局处理子应用挂载
"""
# 挂载静态文件
mount_staticfiles(app)

10
ruoyi-fastapi-backend/sub_applications/staticfiles.py

@ -0,0 +1,10 @@
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from config.env import UploadConfig
def mount_staticfiles(app: FastAPI):
"""
挂载静态文件
"""
app.mount(f"{UploadConfig.UPLOAD_PREFIX}", StaticFiles(directory=f"{UploadConfig.UPLOAD_PATH}"), name="profile")

6
ruoyi-fastapi-backend/utils/common_util.py

@ -5,6 +5,7 @@ from openpyxl import Workbook
from openpyxl.styles import Alignment, PatternFill
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation
from sqlalchemy.engine.row import Row
from typing import List
from config.env import CachePathConfig
@ -66,7 +67,10 @@ class CamelCaseUtil:
return {cls.__to_camel_case(k): v for k, v in result.items()}
# 如果是一组字典或其他类型的列表,遍历列表进行转换
elif isinstance(result, list):
return [cls.transform_result(row) if isinstance(row, dict) else cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) for row in result]
return [cls.transform_result(row) if isinstance(row, (dict, Row)) else (cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) if row else row) for row in result]
# 如果是sqlalchemy的Row实例,遍历Row进行转换
elif isinstance(result, Row):
return [cls.transform_result(row) if isinstance(row, dict) else (cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) if row else row) for row in result]
# 如果是其他类型,如模型实例,先转换为字典
else:
return cls.transform_result({c.name: getattr(result, c.name) for c in result.__table__.columns})

100
ruoyi-fastapi-backend/utils/page_util.py

@ -1,30 +1,9 @@
import math
from typing import Optional, List
from sqlalchemy.orm.query import Query
from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_camel
class PageModel(BaseModel):
"""
分页模型
"""
offset: int
page_num: int
page_size: int
total: int
has_next: bool
class PageObjectResponse(BaseModel):
"""
用户管理列表分页查询返回模型
"""
rows: List = []
page_num: int
page_size: int
total: int
has_next: bool
from utils.common_util import CamelCaseUtil
class PageResponseModel(BaseModel):
@ -40,33 +19,64 @@ class PageResponseModel(BaseModel):
has_next: Optional[bool] = None
def get_page_info(offset: int, page_num: int, page_size: int, count: int):
class PageUtil:
"""
分页工具类
"""
@classmethod
def get_page_obj(cls, data_list: List, page_num: int, page_size: int):
"""
根据分页参数获取分页信息
:param offset: 起始数据位置
输入数据列表data_list和分页信息返回分页数据列表结果
:param data_list: 原始数据列表
:param page_num: 当前页码
:param page_size: 当前页面数据量
:param count: 数据总数
:return: 分页信息对象
:return: 分页数据对象
"""
has_next = False
if offset >= count:
res_offset_1 = (page_num - 2) * page_size
if res_offset_1 < 0:
res_offset = 0
res_page_num = 1
else:
res_offset = res_offset_1
res_page_num = page_num - 1
else:
res_offset = offset
if (res_offset + page_size) < count:
has_next = True
res_page_num = page_num
# 计算起始索引和结束索引
start = (page_num - 1) * page_size
end = page_num * page_size
result = dict(offset=res_offset, page_num=res_page_num, page_size=page_size, total=count, has_next=has_next)
# 根据计算得到的起始索引和结束索引对数据列表进行切片
paginated_data = data_list[start:end]
has_next = True if math.ceil(len(data_list) / page_size) > page_num else False
return PageModel(**result)
result = PageResponseModel(
rows=paginated_data,
pageNum=page_num,
pageSize=page_size,
total=len(data_list),
hasNext=has_next
)
return result
@classmethod
def paginate(cls, query: Query, page_num: int, page_size: int, is_page: bool = False):
"""
输入查询语句和分页信息返回分页数据列表结果
:param query: sqlalchemy查询语句
:param page_num: 当前页码
:param page_size: 当前页面数据量
:param is_page: 是否开启分页
:return: 分页数据对象
"""
if is_page:
total = query.count()
paginated_data = query.offset((page_num - 1) * page_size).limit(page_size).all()
has_next = True if math.ceil(len(paginated_data) / page_size) > page_num else False
result = PageResponseModel(
rows=CamelCaseUtil.transform_result(paginated_data),
pageNum=page_num,
pageSize=page_size,
total=total,
hasNext=has_next
)
else:
no_paginated_data = query.all()
result = CamelCaseUtil.transform_result(no_paginated_data)
return result
def get_page_obj(data_list: List, page_num: int, page_size: int):
@ -94,5 +104,3 @@ def get_page_obj(data_list: List, page_num: int, page_size: int):
)
return result

112
ruoyi-fastapi-backend/utils/response_util.py

@ -187,115 +187,3 @@ class ResponseUtil:
status_code=status.HTTP_200_OK,
content=data
)
def response_200(*, data: Any = None, message="获取成功") -> Response:
return JSONResponse(
status_code=status.HTTP_200_OK,
content=jsonable_encoder(
{
'code': 200,
'message': message,
'data': data,
'success': 'true',
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
)
)
def response_400(*, data: Any = None, message: str = "获取失败") -> Response:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=jsonable_encoder(
{
'code': 400,
'message': message,
'data': data,
'success': 'false',
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
)
)
def response_401(*, data: Any = None, message: str = "获取失败") -> Response:
return JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
content=jsonable_encoder(
{
'code': 401,
'message': message,
'data': data,
'success': 'false',
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
)
)
def response_403(*, data: Any = None, message: str = "获取失败") -> Response:
return JSONResponse(
status_code=status.HTTP_403_FORBIDDEN,
content=jsonable_encoder(
{
'code': 403,
'message': message,
'data': data,
'success': 'false',
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
)
)
def response_500(*, data: Any = None, message: str = "接口异常") -> Response:
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content=jsonable_encoder(
{
'code': 500,
'message': message,
'data': data,
'success': 'false',
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
)
)
def streaming_response_200(*, data: Any = None):
return StreamingResponse(
status_code=status.HTTP_200_OK,
content=data,
)
class AuthException(Exception):
"""
自定义令牌异常AuthException
"""
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message
class PermissionException(Exception):
"""
自定义权限异常PermissionException
"""
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message
class LoginException(Exception):
"""
自定义登录异常LoginException
"""
def __init__(self, data: str = None, message: str = None):
self.data = data
self.message = message

8
ruoyi-fastapi-frontend/package.json

@ -16,9 +16,12 @@
"url": "https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@antv/g2plot": "^2.4.31",
"@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1",
"ant-design-vue": "^4.1.1",
"axios": "0.27.2",
"echarts": "5.4.3",
"element-plus": "2.4.3",
@ -35,11 +38,12 @@
"devDependencies": {
"@vitejs/plugin-vue": "4.5.0",
"@vue/compiler-sfc": "3.3.9",
"less": "^4.2.0",
"sass": "1.69.5",
"unplugin-auto-import": "0.17.1",
"unplugin-vue-setup-extend-plus": "1.0.0",
"vite": "5.0.4",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1",
"unplugin-vue-setup-extend-plus": "1.0.0"
"vite-plugin-svg-icons": "2.0.1"
}
}

2
ruoyi-fastapi-frontend/src/components/RuoYi/Git/index.vue

@ -5,7 +5,7 @@
</template>
<script setup>
const url = ref('https://gitee.com/y_project/RuoYi-Vue');
const url = ref('https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI');
function goto() {
window.open(url.value)

2
ruoyi-fastapi-frontend/src/router/index.js

@ -64,7 +64,7 @@ export const constantRoutes = [
children: [
{
path: '/index',
component: () => import('@/views/index'),
component: () => import('@/views/dashboard/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}

68
ruoyi-fastapi-frontend/src/views/dashboard/editable-link-group.vue

@ -0,0 +1,68 @@
<template>
<div class="linkGroup">
<a v-for="(item, index) in links" :key="index" :href="item.href">
{{ item.title }}
</a>
<a-button size="small" type="primary" ghost>
<PlusOutlined /> 添加
</a-button>
</div>
</template>
<script>
import { Button } from "ant-design-vue";
export default {
components: {
AButton: Button,
},
};
</script>
<script setup>
import { PlusOutlined } from "@ant-design/icons-vue";
const links = [
{
title: "操作一",
href: "",
},
{
title: "操作二",
href: "",
},
{
title: "操作三",
href: "",
},
{
title: "操作四",
href: "",
},
{
title: "操作五",
href: "",
},
{
title: "操作六",
href: "",
},
];
</script>
<style scoped lang="less">
.linkGroup {
padding: 20px 0 8px 24px;
font-size: 0;
& > a {
display: inline-block;
width: 25%;
margin-bottom: 13px;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
&:hover {
color: #1890ff;
}
}
}
</style>

738
ruoyi-fastapi-frontend/src/views/dashboard/index.vue

@ -0,0 +1,738 @@
<template>
<div>
<div class="pageHeaderContent">
<div class="avatar">
<a-avatar size="large" :src="currentUser.avatar" />
</div>
<div class="content">
<div class="contentTitle">
早安
{{ currentUser.name }}
祝你开心每一天
</div>
<div>{{ currentUser.title }} |{{ currentUser.group }}</div>
</div>
<div class="extraContent">
<div class="statItem">
<a-statistic title="项目数" :value="56" />
</div>
<div class="statItem">
<a-statistic title="团队内排名" :value="8" suffix="/ 24" />
</div>
<div class="statItem">
<a-statistic title="项目访问" :value="2223" />
</div>
</div>
</div>
<div style="padding: 10px">
<a-row :gutter="24">
<a-col :xl="16" :lg="24" :md="24" :sm="24" :xs="24">
<a-card
class="projectList"
:style="{ marginBottom: '24px' }"
title="进行中的项目"
:bordered="false"
:loading="false"
:body-style="{ padding: 0 }"
>
<template #extra>
<a href=""> <span style="color: #1890ff">全部项目</span> </a>
</template>
<a-card-grid
v-for="item in projectNotice"
:key="item.id"
class="projectGrid"
>
<a-card
:body-style="{ padding: 0 }"
style="box-shadow: none"
:bordered="false"
>
<a-card-meta :description="item.description" class="w-full">
<template #title>
<div class="cardTitle">
<a-avatar size="small" :src="item.logo" />
<a :href="item.href">
{{ item.title }}
</a>
</div>
</template>
</a-card-meta>
<div class="projectItemContent">
<a :href="item.memberLink">
{{ item.member || "" }}
</a>
<span class="datetime" ml-2 :title="item.updatedAt">
{{ item.updatedAt }}
</span>
</div>
</a-card>
</a-card-grid>
</a-card>
<a-card
:body-style="{ padding: 0 }"
:bordered="false"
class="activeCard"
title="动态"
:loading="false"
>
<a-list :data-source="activities" class="activitiesList">
<template #renderItem="{ item }">
<a-list-item :key="item.id">
<a-list-item-meta>
<template #title>
<span>
<a class="username">{{ item.user.name }}</a
>&nbsp;
<span class="event">
<span>{{ item.template1 }}</span
>&nbsp;
<a href="" style="color: #1890ff">
{{ item?.group?.name }} </a
>&nbsp; <span>{{ item.template2 }}</span
>&nbsp;
<a href="" style="color: #1890ff">
{{ item?.project?.name }}
</a>
</span>
</span>
</template>
<template #avatar>
<a-avatar :src="item.user.avatar" />
</template>
<template #description>
<span class="datetime" :title="item.updatedAt">
{{ item.updatedAt }}
</span>
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-card>
</a-col>
<a-col :xl="8" :lg="24" :md="24" :sm="24" :xs="24">
<a-card
:style="{ marginBottom: '24px' }"
title="快速开始 / 便捷导航"
:bordered="false"
:body-style="{ padding: 0 }"
>
<EditableLinkGroup />
</a-card>
<a-card
:style="{ marginBottom: '24px' }"
:bordered="false"
title="XX 指数"
>
<div class="chart">
<div ref="radarContainer" />
</div>
</a-card>
<a-card
:body-style="{ paddingTop: '12px', paddingBottom: '12px' }"
:bordered="false"
title="团队"
>
<div class="members">
<a-row :gutter="48">
<a-col
v-for="item in projectNotice"
:key="`members-item-${item.id}`"
:span="12"
>
<a :href="item.href">
<a-avatar :src="item.logo" size="small" />
<span class="member">{{ item.member }}</span>
</a>
</a-col>
</a-row>
</div>
</a-card>
</a-col>
</a-row>
</div>
</div>
</template>
<script>
import {
Statistic,
Row,
Col,
Card,
CardGrid,
CardMeta,
List,
ListItem,
ListItemMeta,
Avatar,
} from "ant-design-vue";
import 'ant-design-vue/dist/reset.css';
export default {
components: {
AStatistic: Statistic,
ARow: Row,
ACol: Col,
ACard: Card,
ACardGrid: CardGrid,
ACardMeta: CardMeta,
AList: List,
AListItem: ListItem,
AListItemMeta: ListItemMeta,
AAvatar: Avatar,
},
};
</script>
<script setup>
import { Radar } from "@antv/g2plot";
import EditableLinkGroup from "./editable-link-group.vue";
defineOptions({
name: "DashBoard",
});
const currentUser = {
avatar: "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
name: "吴彦祖",
userid: "00000001",
email: "antdesign@alipay.com",
signature: "海纳百川,有容乃大",
title: "交互专家",
group: "蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED",
};
const projectNotice = [
{
id: "xxx1",
title: "Alipay",
logo: "https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png",
description: "那是一种内在的东西,他们到达不了,也无法触及的",
updatedAt: "几秒前",
member: "科学搬砖组",
href: "",
memberLink: "",
},
{
id: "xxx2",
title: "Angular",
logo: "https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png",
description: "希望是一个好东西,也许是最好的,好东西是不会消亡的",
updatedAt: "6 年前",
member: "全组都是吴彦祖",
href: "",
memberLink: "",
},
{
id: "xxx3",
title: "Ant Design",
logo: "https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png",
description: "城镇中有那么多的酒馆,她却偏偏走进了我的酒馆",
updatedAt: "几秒前",
member: "中二少女团",
href: "",
memberLink: "",
},
{
id: "xxx4",
title: "Ant Design Pro",
logo: "https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png",
description: "那时候我只会想自己想要什么,从不想自己拥有什么",
updatedAt: "6 年前",
member: "程序员日常",
href: "",
memberLink: "",
},
{
id: "xxx5",
title: "Bootstrap",
logo: "https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png",
description:
"凛冬将至",
updatedAt: "6 年前",
member: "高逼格设计天团",
href: "",
memberLink: "",
},
{
id: "xxx6",
title: "React",
logo: "https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png",
description: "生命就像一盒巧克力,结果往往出人意料",
updatedAt: "6 年前",
member: "骗你来学计算机",
href: "",
memberLink: "",
},
];
const activities = [
{
id: "trend-1",
updatedAt: "几秒前",
user: {
name: "曲丽丽",
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
},
group: {
name: "高逼格设计天团",
link: "http://github.com/",
},
project: {
name: "六月迭代",
link: "http://github.com/",
},
template1: "在",
template2: "新建项目",
},
{
id: "trend-2",
updatedAt: "几秒前",
user: {
name: "付小小",
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png",
},
group: {
name: "高逼格设计天团",
link: "http://github.com/",
},
project: {
name: "六月迭代",
link: "http://github.com/",
},
template1: "在",
template2: "新建项目",
},
{
id: "trend-3",
updatedAt: "几秒前",
user: {
name: "林东东",
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png",
},
group: {
name: "中二少女团",
link: "http://github.com/",
},
project: {
name: "六月迭代",
link: "http://github.com/",
},
template1: "在",
template2: "新建项目",
},
{
id: "trend-4",
updatedAt: "几秒前",
user: {
name: "周星星",
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png",
},
group: {
name: "5 月日常迭代",
link: "http://github.com/",
},
template1: "将",
template2: "更新至已发布状态",
},
{
id: "trend-5",
updatedAt: "几秒前",
user: {
name: "朱偏右",
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png",
},
group: {
name: "工程效能",
link: "http://github.com/",
},
project: {
name: "留言",
link: "http://github.com/",
},
template1: "在",
template2: "发布了",
},
{
id: "trend-6",
updatedAt: "几秒前",
user: {
name: "乐哥",
avatar:
"https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png",
},
group: {
name: "程序员日常",
link: "http://github.com/",
},
project: {
name: "品牌迭代",
link: "http://github.com/",
},
template1: "在",
template2: "新建项目",
},
];
const radarContainer = ref();
const radarData = [
{
name: "个人",
label: "引用",
value: 10,
},
{
name: "个人",
label: "口碑",
value: 8,
},
{
name: "个人",
label: "产量",
value: 4,
},
{
name: "个人",
label: "贡献",
value: 5,
},
{
name: "个人",
label: "热度",
value: 7,
},
{
name: "团队",
label: "引用",
value: 3,
},
{
name: "团队",
label: "口碑",
value: 9,
},
{
name: "团队",
label: "产量",
value: 6,
},
{
name: "团队",
label: "贡献",
value: 3,
},
{
name: "团队",
label: "热度",
value: 1,
},
{
name: "部门",
label: "引用",
value: 4,
},
{
name: "部门",
label: "口碑",
value: 1,
},
{
name: "部门",
label: "产量",
value: 6,
},
{
name: "部门",
label: "贡献",
value: 5,
},
{
name: "部门",
label: "热度",
value: 7,
},
];
let radar;
onMounted(() => {
radar = new Radar(radarContainer.value, {
data: radarData,
xField: "label",
yField: "value",
seriesField: "name",
point: {
size: 4,
},
legend: {
layout: "horizontal",
position: "bottom",
},
});
radar.render();
});
onBeforeUnmount(() => {
radar?.destroy?.();
});
</script>
<style scoped lang="less">
.textOverflow() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
}
// mixins for clearfix
// ------------------------
.clearfix() {
zoom: 1;
&::before,
&::after {
display: table;
content: " ";
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
}
.activitiesList {
padding: 0 24px 8px 24px;
.username {
color: rgba(0, 0, 0, 0.65);
}
.event {
font-weight: normal;
}
}
.pageHeaderContent {
display: flex;
padding: 12px;
margin-bottom: 24px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
.avatar {
flex: 0 1 72px;
& > span {
display: block;
width: 72px;
height: 72px;
border-radius: 72px;
}
}
.content {
position: relative;
top: 4px;
flex: 1 1 auto;
margin-left: 24px;
color: rgba(0, 0, 0, 0.45);
line-height: 22px;
.contentTitle {
margin-bottom: 12px;
color: rgba(0, 0, 0, 0.85);
font-weight: 500;
font-size: 20px;
line-height: 28px;
}
}
}
.extraContent {
.clearfix();
float: right;
white-space: nowrap;
.statItem {
position: relative;
display: inline-block;
padding: 0 32px;
> p:first-child {
margin-bottom: 4px;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
line-height: 22px;
}
> p {
margin: 0;
color: rgba(0, 0, 0, 0.85);
font-size: 30px;
line-height: 38px;
> span {
color: rgba(0, 0, 0, 0.45);
font-size: 20px;
}
}
&::after {
position: absolute;
top: 8px;
right: 0;
width: 1px;
height: 40px;
background-color: #e8e8e8;
content: "";
}
&:last-child {
padding-right: 0;
&::after {
display: none;
}
}
}
}
.members {
a {
display: block;
height: 24px;
margin: 12px 0;
color: rgba(0, 0, 0, 0.65);
transition: all 0.3s;
.textOverflow();
.member {
margin-left: 12px;
font-size: 14px;
line-height: 24px;
vertical-align: top;
}
&:hover {
color: #1890ff;
}
}
}
.projectList {
:deep(.ant-card-meta-description) {
height: 44px;
overflow: hidden;
color: rgba(0, 0, 0, 0.45);
line-height: 22px;
}
.cardTitle {
font-size: 0;
a {
display: inline-block;
height: 24px;
margin-left: 12px;
color: rgba(0, 0, 0, 0.85);
font-size: 14px;
line-height: 24px;
vertical-align: top;
&:hover {
color: #1890ff;
}
}
}
.projectGrid {
width: 33.33%;
}
.projectItemContent {
display: flex;
flex-basis: 100%;
height: 20px;
margin-top: 8px;
overflow: hidden;
font-size: 12px;
line-height: 20px;
.textOverflow();
a {
display: inline-block;
flex: 1 1 0;
color: rgba(0, 0, 0, 0.45);
.textOverflow();
&:hover {
color: #1890ff;
}
}
.datetime {
flex: 0 0 auto;
float: right;
color: rgba(0, 0, 0, 0.25);
}
}
}
.datetime {
color: rgba(0, 0, 0, 0.25);
}
@media screen and (max-width: 1200px) and (min-width: 992px) {
.activeCard {
margin-bottom: 24px;
}
.members {
margin-bottom: 0;
}
.extraContent {
margin-left: -44px;
.statItem {
padding: 0 16px;
}
}
}
@media screen and (max-width: 992px) {
.activeCard {
margin-bottom: 24px;
}
.members {
margin-bottom: 0;
}
.extraContent {
float: none;
margin-right: 0;
.statItem {
padding: 0 16px;
text-align: left;
&::after {
display: none;
}
}
}
}
@media screen and (max-width: 768px) {
.extraContent {
margin-left: -16px;
}
.projectList {
.projectGrid {
width: 50%;
}
}
}
@media screen and (max-width: 576px) {
.pageHeaderContent {
display: block;
.content {
margin-left: 0;
}
}
.extraContent {
.statItem {
float: none;
}
}
}
@media screen and (max-width: 480px) {
.projectList {
.projectGrid {
width: 100%;
}
}
}
</style>

1061
ruoyi-fastapi-frontend/src/views/index.vue

File diff suppressed because it is too large

5
ruoyi-fastapi-frontend/src/views/login.vue

@ -76,8 +76,8 @@ const router = useRouter();
const { proxy } = getCurrentInstance();
const loginForm = ref({
username: "admin",
password: "admin123",
username: "",
password: "",
rememberMe: false,
code: "",
uuid: ""
@ -140,6 +140,7 @@ function handleLogin() {
function getCode() {
getCodeImg().then(res => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
register.value = res.registerEnabled === undefined ? false : res.registerEnabled;
if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img;
loginForm.value.uuid = res.uuid;

4
ruoyi-fastapi-frontend/src/views/register.vue

@ -1,7 +1,7 @@
<template>
<div class="register">
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">若依后台管理系统</h3>
<h3 class="title">vfadmin后台管理系统</h3>
<el-form-item prop="username">
<el-input
v-model="registerForm.username"
@ -70,7 +70,7 @@
</el-form>
<!-- 底部 -->
<div class="el-register-footer">
<span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
<span>Copyright © 2024 insistence.tech All Rights Reserved.</span>
</div>
</div>
</template>

Loading…
Cancel
Save