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. 26
      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. 169
      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. 24
      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. 104
      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 1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. xxxx 2. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
3. xxxx 3. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
4. 部门管理:配置系统组织机构(公司、部门、小组)。
5. 岗位管理:配置系统用户所属担任职务。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
14. 缓存监控:对系统的缓存信息查询,命令统计等。
15. 系统接口:根据业务代码自动生成相关的api接口文档。
#### 使用说明 ## 演示图
1. xxxx <table>
2. xxxx <tr>
3. xxxx <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) cd RuoYi-Vue3-FastAPI
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/) ```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 import uvicorn
from contextlib import asynccontextmanager from server import app, AppConfig
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
@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__': 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 urllib.parse import quote_plus
from config.env import DataBaseConfig from config.env import DataBaseConfig
SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.USERNAME}:{quote_plus(DataBaseConfig.PASSWORD)}@" \ SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@" \
f"{DataBaseConfig.HOST}:{DataBaseConfig.PORT}/{DataBaseConfig.DB}" f"{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}"
engine = create_engine( engine = create_engine(
SQLALCHEMY_DATABASE_URL, echo=True SQLALCHEMY_DATABASE_URL, echo=True

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

@ -1,39 +1,57 @@
import os 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配置 Jwt配置
""" """
SECRET_KEY = "b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55" jwt_secret_key: str = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
ALGORITHM = "HS256" jwt_algorithm: str = 'HS256'
ACCESS_TOKEN_EXPIRE_MINUTES = 1440 jwt_expire_minutes: int = 1440
REDIS_TOKEN_EXPIRE_MINUTES = 30 jwt_redis_expire_minutes: int = 30
class DataBaseConfig: class DataBaseSettings(BaseSettings):
""" """
数据库配置 数据库配置
""" """
HOST = "127.0.0.1" db_host: str = '127.0.0.1'
PORT = 3306 db_port: int = 3306
USERNAME = 'root' db_username: str = 'root'
PASSWORD = 'mysqlroot' db_password: str = 'mysqlroot'
DB = 'ruoyi-fastapi' db_database: str = 'ruoyi-fastapi'
class RedisConfig: class RedisSettings(BaseSettings):
""" """
Redis配置 Redis配置
""" """
HOST = "127.0.0.1" redis_host: str = '127.0.0.1'
PORT = 6379 redis_port: int = 6379
USERNAME = '' redis_username: str = ''
PASSWORD = '' redis_password: str = ''
DB = 2 redis_database: int = 2
class UploadConfig: class UploadSettings:
""" """
上传配置 上传配置
""" """
@ -80,3 +98,92 @@ class RedisInitKeyConfig:
ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'} ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'}
PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'} PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'}
SMS_CODE = {'key': 'sms_code', '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()

26
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.dict_service import DictDataService
from module_admin.service.config_service import ConfigService from module_admin.service.config_service import ConfigService
from config.env import RedisConfig from config.env import RedisConfig
@ -19,15 +20,26 @@ class RedisUtil:
""" """
logger.info("开始连接redis...") logger.info("开始连接redis...")
redis = await aioredis.from_url( redis = await aioredis.from_url(
url=f"redis://{RedisConfig.HOST}", url=f"redis://{RedisConfig.redis_host}",
port=RedisConfig.PORT, port=RedisConfig.redis_port,
username=RedisConfig.USERNAME, username=RedisConfig.redis_username,
password=RedisConfig.PASSWORD, password=RedisConfig.redis_password,
db=RedisConfig.DB, db=RedisConfig.redis_database,
encoding="utf-8", encoding="utf-8",
decode_responses=True decode_responses=True
) )
logger.info("redis连接成功") 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 return redis
@classmethod @classmethod

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

@ -70,11 +70,11 @@ job_stores = {
'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine), 'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine),
'redis': RedisJobStore( 'redis': RedisJobStore(
**dict( **dict(
host=RedisConfig.HOST, host=RedisConfig.redis_host,
port=RedisConfig.PORT, port=RedisConfig.redis_port,
username=RedisConfig.USERNAME, username=RedisConfig.redis_username,
password=RedisConfig.PASSWORD, password=RedisConfig.redis_password,
db=RedisConfig.DB 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 fastapi import Depends
from module_admin.entity.vo.user_vo import CurrentUserModel from module_admin.entity.vo.user_vo import CurrentUserModel
from module_admin.service.login_service import LoginService from module_admin.service.login_service import LoginService
from utils.response_util import PermissionException from exceptions.exception import PermissionException
class CheckUserInterfaceAuth: 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): async def get_captcha_image(request: Request):
try: try:
captcha_enabled = True if await request.app.state.redis.get(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == '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
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()) session_id = str(uuid.uuid4())
captcha_result = CaptchaService.create_captcha_image_service() captcha_result = CaptchaService.create_captcha_image_service()
image = captcha_result[0] 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)) 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}的会话获取图片验证码成功') logger.info(f'编号为{session_id}的会话获取图片验证码成功')
return ResponseUtil.success( 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: except Exception as e:
logger.exception(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'))]) @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)): async def get_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 config_page_query_result = ConfigService.get_config_list_services(query_db, config_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=config_page_query_result) return ResponseUtil.success(model_content=config_page_query_result)
except Exception as e: except Exception as e:
@ -125,9 +122,8 @@ async def query_system_config(request: Request, config_key: str):
@log_decorator(title='参数管理', business_type=5) @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)): async def export_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try: 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) config_export_result = ConfigService.export_config_list_services(config_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(config_export_result)) 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 module_admin.service.dict_service import *
from utils.response_util import * from utils.response_util import *
from utils.log_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 utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.annotation.log_annotation import log_decorator 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'))]) @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)): 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: try:
dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 dict_type_page_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=dict_type_page_query_result) return ResponseUtil.success(model_content=dict_type_page_query_result)
except Exception as e: 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'))]) @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)): async def query_system_dict_type_options(request: Request, query_db: Session = Depends(get_db)):
try: 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'获取成功') logger.info(f'获取成功')
return ResponseUtil.success(data=dict_type_query_result) return ResponseUtil.success(data=dict_type_query_result)
except Exception as e: 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) @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)): 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: 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) dict_type_export_result = DictTypeService.export_dict_type_list_services(dict_type_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result)) 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'))]) @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)): 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: try:
dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 dict_data_page_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=dict_data_page_query_result) return ResponseUtil.success(model_content=dict_data_page_query_result)
except Exception as e: 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) @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)): 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: 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) dict_data_export_result = DictDataService.export_dict_data_list_services(dict_data_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(dict_data_export_result)) 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'))]) @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)): async def get_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
job_query = JobModel(**job_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 notice_page_query_result = JobService.get_job_list_services(query_db, job_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=notice_page_query_result) return ResponseUtil.success(model_content=notice_page_query_result)
except Exception as e: 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) @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)): async def export_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try: 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) job_export_result = await JobService.export_job_list_services(request, job_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(job_export_result)) 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'))]) @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)): 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: try:
job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 job_log_page_query_result = JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=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)
logger.info('获取成功') 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: except Exception as e:
logger.exception(e) logger.exception(e)
return ResponseUtil.error(msg=str(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) @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)): 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: 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_query_result = JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False)
job_log_export_result = JobLogService.export_job_log_list_services(query_db, job_log_query_result) job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result)) return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result))
except Exception as e: 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'))]) @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)): 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: try:
operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 operation_log_page_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=operation_log_page_query_result) return ResponseUtil.success(model_content=operation_log_page_query_result)
except Exception as e: 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) @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)): 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: 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) operation_log_export_result = await OperationLogService.export_operation_log_list_services(request, operation_log_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result)) 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'))]) @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)): 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: try:
login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 login_log_page_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=login_log_page_query_result) return ResponseUtil.success(model_content=login_log_page_query_result)
except Exception as e: 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) @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)): 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: 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) login_log_export_result = LoginLogService.export_login_log_list_services(login_log_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(login_log_export_result)) 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.service.login_service import *
from module_admin.entity.vo.login_vo import * from module_admin.entity.vo.login_vo import *
from module_admin.dao.login_dao import * from module_admin.dao.login_dao import *
from module_admin.annotation.log_annotation import log_decorator
from config.env import JwtConfig, RedisInitKeyConfig from config.env import JwtConfig, RedisInitKeyConfig
from utils.response_util import * from utils.response_util import ResponseUtil
from utils.log_util import * from utils.log_util import *
from module_admin.annotation.log_annotation import log_decorator
from datetime import timedelta from datetime import timedelta
@ -29,7 +29,7 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D
except LoginException as e: except LoginException as e:
return ResponseUtil.failure(msg=e.message) return ResponseUtil.failure(msg=e.message)
try: 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()) session_id = str(uuid.uuid4())
access_token = LoginService.create_access_token( access_token = LoginService.create_access_token(
data={ data={
@ -42,10 +42,11 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D
expires_delta=access_token_expires expires_delta=access_token_expires
) )
await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", access_token, 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, # 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('登录成功') logger.info('登录成功')
# 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug # 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False 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)) return ResponseUtil.error(msg=str(e))
@loginController.post("/getSmsCode", response_model=SmsCode) @loginController.post("/register", response_model=CrudResponseModel)
async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)): async def register_user(request: Request, user_register: UserRegister, query_db: Session = Depends(get_db)):
try: try:
sms_result = await get_sms_code_services(request, query_db, user) user_register_result = await LoginService.register_user_services(request, query_db, user_register)
if sms_result.is_success: if user_register_result.is_success:
logger.info('获取成功') logger.info(user_register_result.message)
return response_200(data=sms_result, message='获取成功') return ResponseUtil.success(data=user_register_result, msg=user_register_result.message)
else: else:
logger.warning(sms_result.message) logger.warning(user_register_result.message)
return response_400(data='', message=sms_result.message) return ResponseUtil.failure(msg=user_register_result.message)
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
return response_500(data="", message=str(e)) return ResponseUtil.error(msg=str(e))
@loginController.post("/forgetPwd", response_model=CrudResponseModel) # @loginController.post("/getSmsCode", response_model=SmsCode)
async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)): # async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)):
try: # try:
forget_user_result = await forget_user_services(request, query_db, forget_user) # sms_result = await LoginService.get_sms_code_services(request, query_db, user)
if forget_user_result.is_success: # if sms_result.is_success:
logger.info(forget_user_result.message) # logger.info('获取成功')
return response_200(data=forget_user_result, message=forget_user_result.message) # return ResponseUtil.success(data=sms_result)
else: # else:
logger.warning(forget_user_result.message) # logger.warning(sms_result.message)
return response_400(data="", message=forget_user_result.message) # return ResponseUtil.failure(msg=sms_result.message)
except Exception as e: # except Exception as e:
logger.exception(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 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") @loginController.post("/logout")
async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme)): async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme)):
try: 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") session_id: str = payload.get("session_id")
await logout_services(request, session_id) await LoginService.logout_services(request, session_id)
logger.info('退出成功') logger.info('退出成功')
return ResponseUtil.success(msg="退出成功") return ResponseUtil.success(msg="退出成功")
except Exception as e: 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'))]) @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)): async def get_system_notice_list(request: Request, notice_page_query: NoticePageQueryModel = Depends(NoticePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
notice_query = NoticeQueryModel(**notice_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 notice_page_query_result = NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=notice_page_query_result) return ResponseUtil.success(model_content=notice_page_query_result)
except Exception as e: 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'))]) @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)): async def get_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
post_query = PostModel(**post_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 post_page_query_result = PostService.get_post_list_services(query_db, post_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=post_page_query_result) return ResponseUtil.success(model_content=post_page_query_result)
except Exception as e: 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) @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)): async def export_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_form), query_db: Session = Depends(get_db)):
try: 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) post_export_result = PostService.export_post_list_services(post_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(post_export_result)) 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 module_admin.service.user_service import UserService, UserRoleQueryModel, UserRolePageQueryModel, CrudUserRoleModel
from utils.response_util import * from utils.response_util import *
from utils.log_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 utils.common_util import bytes2file_response
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
from module_admin.aspect.data_scope import GetDataScope 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'))]) @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)): async def get_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True)) role_page_query_result = RoleService.get_role_list_services(query_db, role_page_query, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=role_page_query_result) return ResponseUtil.success(model_content=role_page_query_result)
except Exception as e: 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) @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)): async def export_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_form), query_db: Session = Depends(get_db)):
try: 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) role_export_result = RoleService.export_role_list_services(role_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(role_export_result)) 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'))]) @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)): async def get_system_allocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True)) role_user_allocated_page_query_result = RoleService.get_role_user_allocated_list_services(query_db, user_role, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=role_user_allocated_page_query_result) return ResponseUtil.success(model_content=role_user_allocated_page_query_result)
except Exception as e: 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'))]) @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)): async def get_system_unallocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
try: try:
role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True)) role_user_unallocated_page_query_result = RoleService.get_role_user_unallocated_list_services(query_db, user_role, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=role_user_unallocated_page_query_result) return ResponseUtil.success(model_content=role_user_unallocated_page_query_result)
except Exception as e: 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.login_service import LoginService
from module_admin.service.user_service import * from module_admin.service.user_service import *
from module_admin.service.dept_service import DeptService 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.response_util import *
from utils.log_util import * from utils.log_util import *
from utils.common_util import bytes2file_response 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'))]) @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'))): 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: try:
user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True)) # 获取分页数据
# 获取全量数据 user_page_query_result = UserService.get_user_list_services(query_db, user_page_query, data_scope_sql, is_page=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)
logger.info('获取成功') logger.info('获取成功')
return ResponseUtil.success(model_content=user_page_query_result) return ResponseUtil.success(model_content=user_page_query_result)
except Exception as e: 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) @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'))): 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: 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) user_export_result = UserService.export_user_list_services(user_query_result)
logger.info('导出成功') logger.info('导出成功')
return ResponseUtil.streaming(data=bytes2file_response(user_export_result)) 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 sqlalchemy.orm import Session
from module_admin.entity.do.config_do import SysConfig from module_admin.entity.do.config_do import SysConfig
from module_admin.entity.vo.config_vo import * from module_admin.entity.vo.config_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time from datetime import datetime, time
@ -39,14 +40,15 @@ class ConfigDao:
return config_info return config_info
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 参数配置列表信息对象 :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, .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_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, 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))) 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 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 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.do.dict_do import SysDictType, SysDictData
from module_admin.entity.vo.dict_vo import * from module_admin.entity.vo.dict_vo import *
from utils.time_format_util import list_format_datetime from utils.time_format_util import list_format_datetime
from utils.page_util import PageUtil
from datetime import datetime, time from datetime import datetime, time
@ -52,14 +53,15 @@ class DictTypeDao:
return list_format_datetime(dict_type_info) return list_format_datetime(dict_type_info)
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典类型列表信息对象 :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, .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.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, 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))) 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 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 return dict_type_list
@ -147,20 +150,22 @@ class DictDataDao:
return dict_data_info return dict_data_info
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典数据列表信息对象 :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, .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.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 SysDictData.status == query_object.status if query_object.status else True
) \ ) \
.order_by(SysDictData.dict_sort) \ .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 return dict_data_list

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

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from module_admin.entity.do.job_do import SysJob from module_admin.entity.do.job_do import SysJob
from module_admin.entity.vo.job_vo import * from module_admin.entity.vo.job_vo import *
from utils.page_util import PageUtil
class JobDao: class JobDao:
@ -40,19 +41,21 @@ class JobDao:
return job_info return job_info
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务列表信息对象 :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, .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.job_group == query_object.job_group if query_object.job_group else True,
SysJob.status == query_object.status if query_object.status 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 return job_list

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

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from module_admin.entity.do.job_do import SysJobLog from module_admin.entity.do.job_do import SysJobLog
from module_admin.entity.vo.job_vo import * from module_admin.entity.vo.job_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time from datetime import datetime, time
@ -10,14 +11,15 @@ class JobLogDao:
""" """
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务日志列表信息对象 :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, .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.job_group == query_object.job_group if query_object.job_group else True,
SysJobLog.status == query_object.status if query_object.status 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))) 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 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 return job_log_list

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

@ -1,7 +1,7 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from module_admin.entity.do.log_do import SysOperLog, SysLogininfor from module_admin.entity.do.log_do import SysOperLog, SysLogininfor
from module_admin.entity.vo.log_vo import * 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 from datetime import datetime, time
@ -10,14 +10,15 @@ class OperationLogDao:
操作日志管理模块数据库操作层 操作日志管理模块数据库操作层
""" """
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 操作日志列表信息对象 :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, .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.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, 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))) 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 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 return operation_log_list
@ -74,14 +76,15 @@ class LoginLogDao:
""" """
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 登录日志列表信息对象 :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, .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.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, 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))) 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 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 return login_log_list

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

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from module_admin.entity.do.notice_do import SysNotice from module_admin.entity.do.notice_do import SysNotice
from module_admin.entity.vo.notice_vo import * from module_admin.entity.vo.notice_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time from datetime import datetime, time
@ -40,14 +41,15 @@ class NoticeDao:
return notice_info return notice_info
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 通知公告列表信息对象 :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, .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.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, 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))) 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 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 return notice_list

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

@ -1,6 +1,7 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from module_admin.entity.do.post_do import SysPost from module_admin.entity.do.post_do import SysPost
from module_admin.entity.vo.post_vo import * from module_admin.entity.vo.post_vo import *
from utils.page_util import PageUtil
class PostDao: class PostDao:
@ -54,20 +55,22 @@ class PostDao:
return post_info return post_info
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 岗位列表信息对象 :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, .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.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 SysPost.status == query_object.status if query_object.status else True
) \ ) \
.order_by(SysPost.post_sort) \ .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 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.role_do import SysRole, SysRoleMenu, SysRoleDept
from module_admin.entity.do.dept_do import SysDept from module_admin.entity.do.dept_do import SysDept
from module_admin.entity.vo.role_vo import * from module_admin.entity.vo.role_vo import *
from utils.page_util import PageUtil
from datetime import datetime, time from datetime import datetime, time
@ -85,14 +86,15 @@ class RoleDao:
return role_info return role_info
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 角色列表信息对象 :return: 角色列表信息对象
""" """
role_list = db.query(SysRole) \ query = db.query(SysRole) \
.filter(SysRole.del_flag == 0, .filter(SysRole.del_flag == 0,
SysRole.role_name.like(f'%{query_object.role_name}%') if query_object.role_name else True, 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, 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 if query_object.begin_time and query_object.end_time else True
) \ ) \
.order_by(SysRole.role_sort) \ .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 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.post_do import SysPost
from module_admin.entity.do.menu_do import SysMenu from module_admin.entity.do.menu_do import SysMenu
from module_admin.entity.vo.user_vo import * 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 from datetime import datetime, time
@ -137,15 +137,16 @@ class UserDao:
return results return results
@classmethod @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 db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param data_scope_sql: 数据权限对应的查询sql语句 :param data_scope_sql: 数据权限对应的查询sql语句
:param is_page: 是否开启分页
:return: 用户列表信息对象 :return: 用户列表信息对象
""" """
user_list = db.query(SysUser, SysDept) \ query = db.query(SysUser, SysDept) \
.filter(SysUser.del_flag == 0, .filter(SysUser.del_flag == 0,
or_(SysUser.dept_id == query_object.dept_id, SysUser.dept_id.in_( 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)) 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) eval(data_scope_sql)
) \ ) \
.outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \ .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 return user_list
@ -227,35 +229,15 @@ class UserDao:
return allocated_role_list return allocated_role_list
@classmethod @classmethod
def get_user_role_unallocated_list_by_user_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: 用户角色查询对象
: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):
""" """
根据角色id获取已分配的用户列表信息 根据角色id获取已分配的用户列表信息
:param db: orm对象 :param db: orm对象
:param query_object: 用户角色查询对象 :param query_object: 用户角色查询对象
:param is_page: 是否开启分页
:return: 角色已分配的用户列表信息 :return: 角色已分配的用户列表信息
""" """
allocated_user_list = db.query(SysUser) \ query = db.query(SysUser) \
.filter( .filter(
SysUser.del_flag == 0, SysUser.del_flag == 0,
SysUser.user_id != 1, SysUser.user_id != 1,
@ -264,19 +246,21 @@ class UserDao:
SysUser.user_id.in_( SysUser.user_id.in_(
db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id) 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 return allocated_user_list
@classmethod @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获取未分配的用户列表信息 根据角色id获取未分配的用户列表信息
:param db: orm对象 :param db: orm对象
:param query_object: 用户角色查询对象 :param query_object: 用户角色查询对象
:param is_page: 是否开启分页
:return: 角色未分配的用户列表信息 :return: 角色未分配的用户列表信息
""" """
unallocated_user_list = db.query(SysUser) \ query = db.query(SysUser) \
.filter( .filter(
SysUser.del_flag == 0, SysUser.del_flag == 0,
SysUser.user_id != 1, SysUser.user_id != 1,
@ -285,7 +269,8 @@ class UserDao:
~SysUser.user_id.in_( ~SysUser.user_id.in_(
db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id) 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 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_num: int = 1
page_size: int page_size: int = 10
class DeleteConfigModel(BaseModel): 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_num: int = 1
page_size: int page_size: int = 10
class DeleteDictTypeModel(BaseModel): class DeleteDictTypeModel(BaseModel):
@ -71,7 +71,7 @@ class DeleteDictTypeModel(BaseModel):
dict_ids: str dict_ids: str
class DictDataQueryModel(DictTypeModel): class DictDataQueryModel(DictDataModel):
""" """
字典数据管理不分页查询模型 字典数据管理不分页查询模型
""" """
@ -85,8 +85,8 @@ class DictDataPageQueryModel(DictDataQueryModel):
""" """
字典数据管理分页查询模型 字典数据管理分页查询模型
""" """
page_num: int page_num: int = 1
page_size: int page_size: int = 10
class DeleteDictDataModel(BaseModel): 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_num: int = 1
page_size: int page_size: int = 10
class EditJobModel(JobModel): class EditJobModel(JobModel):
@ -97,8 +97,8 @@ class JobLogPageQueryModel(JobLogQueryModel):
""" """
定时任务日志管理分页查询模型 定时任务日志管理分页查询模型
""" """
page_num: int page_num: int = 1
page_size: int page_size: int = 10
class DeleteJobLogModel(BaseModel): 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_num: int = 1
page_size: int page_size: int = 10
class DeleteOperLogModel(BaseModel): class DeleteOperLogModel(BaseModel):
@ -89,8 +89,8 @@ class LoginLogPageQueryModel(LoginLogQueryModel):
""" """
登录日志管理分页查询模型 登录日志管理分页查询模型
""" """
page_num: int page_num: int = 1
page_size: int page_size: int = 10
class DeleteLoginLogModel(BaseModel): 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 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): class Token(BaseModel):
access_token: str access_token: str
token_type: str token_type: str
@ -23,6 +33,7 @@ class CaptchaCode(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
captcha_enabled: bool captcha_enabled: bool
register_enabled: bool
img: str img: str
uuid: 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_num: int = 1
page_size: int page_size: int = 10
class DeleteNoticeModel(BaseModel): 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_num: int = 1
page_size: int page_size: int = 10
class DeletePostModel(BaseModel): 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_num: int = 1
page_size: int page_size: int = 10
class RoleMenuQueryModel(BaseModel): 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_num: int = 1
page_size: int page_size: int = 10
class AddUserModel(UserModel): class AddUserModel(UserModel):
@ -176,8 +176,8 @@ class UserRolePageQueryModel(UserRoleQueryModel):
""" """
用户角色关联管理分页查询模型 用户角色关联管理分页查询模型
""" """
page_num: int page_num: int = 1
page_size: int page_size: int = 10
class SelectedRoleModel(RoleModel): class SelectedRoleModel(RoleModel):

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

@ -11,16 +11,17 @@ class ConfigService:
""" """
@classmethod @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 获取参数配置列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 参数配置列表信息对象 :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 @classmethod
async def init_cache_sys_config_services(cls, query_db: Session, redis): async def init_cache_sys_config_services(cls, query_db: Session, redis):
@ -35,10 +36,10 @@ class ConfigService:
# 删除匹配的键 # 删除匹配的键
if keys: if keys:
await redis.delete(*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: for config_obj in config_all:
if config_obj.config_type == 'Y': if config_obj.get('configType') == 'Y':
await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.config_key}", config_obj.config_value) await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.get('configKey')}", config_obj.get('configValue'))
@classmethod @classmethod
async def query_config_list_from_cache_services(cls, redis, config_key: str): 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 @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 获取字典类型列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典类型列表信息对象 :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 @classmethod
async def add_dict_type_services(cls, request: Request, query_db: Session, page_object: DictTypeModel): async def add_dict_type_services(cls, request: Request, query_db: Session, page_object: DictTypeModel):
@ -172,16 +173,17 @@ class DictDataService:
""" """
@classmethod @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 获取字典数据列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 字典数据列表信息对象 :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 @classmethod
def query_dict_data_list_services(cls, query_db: Session, dict_type: str): 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.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 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: class JobLogService:
@ -10,16 +10,17 @@ class JobLogService:
""" """
@classmethod @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 获取定时任务日志列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务日志列表信息对象 :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 @classmethod
def add_job_log_services(cls, query_db: Session, page_object: JobLogModel): def add_job_log_services(cls, query_db: Session, page_object: JobLogModel):
@ -79,10 +80,10 @@ class JobLogService:
return CrudResponseModel(**result) return CrudResponseModel(**result)
@staticmethod @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 导出定时任务日志信息service
:param query_db: orm对象 :param request: Request对象
:param job_log_list: 定时任务日志信息列表 :param job_log_list: 定时任务日志信息列表
:return: 定时任务日志信息对应excel的二进制数据 :return: 定时任务日志信息对应excel的二进制数据
""" """
@ -103,17 +104,22 @@ class JobLogService:
} }
data = job_log_list data = job_log_list
job_group_list = DictDataDao.query_dict_data_list(query_db, dict_type='sys_job_group') 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.dict_label, value=item.dict_value) for item in job_group_list] 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_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: for item in data:
if item.get('status') == '0': if item.get('status') == '0':
item['status'] = '正常' item['status'] = '正常'
else: else:
item['status'] = '暂停' item['status'] = '暂停'
if str(item.get('job_group')) in job_group_option_dict.keys(): if str(item.get('jobGroup')) in job_group_option_dict.keys():
item['job_group'] = job_group_option_dict.get(str(item.get('job_group'))).get('label') 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 new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in
data] data]
binary_data = export_list2excel(new_data) binary_data = export_list2excel(new_data)

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

@ -11,16 +11,17 @@ class JobService:
""" """
@classmethod @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 获取定时任务列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 定时任务列表信息对象 :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 @classmethod
def add_job_services(cls, query_db: Session, page_object: JobModel): 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 @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 获取操作日志列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 操作日志列表信息对象 :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 @classmethod
def add_operation_log_services(cls, query_db: Session, page_object: OperLogModel): def add_operation_log_services(cls, query_db: Session, page_object: OperLogModel):
@ -131,16 +132,17 @@ class LoginLogService:
""" """
@classmethod @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 获取登录日志列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 登录日志列表信息对象 :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 @classmethod
def add_login_log_services(cls, query_db: Session, page_object: LogininforModel): def add_login_log_services(cls, query_db: Session, page_object: LogininforModel):

169
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.login_vo import *
from module_admin.entity.vo.common_vo import CrudResponseModel from module_admin.entity.vo.common_vo import CrudResponseModel
from module_admin.dao.login_dao import * 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.common_util import CamelCaseUtil
from utils.pwd_util import * from utils.pwd_util import *
from utils.response_util import * from utils.response_util import *
from utils.message_util import * from utils.message_util import *
from config.get_db import get_db
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
@ -60,8 +61,13 @@ class LoginService:
if login_user.user_name == account_lock: if login_user.user_name == account_lock:
logger.warning("账号已锁定,请稍后再试") logger.warning("账号已锁定,请稍后再试")
raise LoginException(data="", message="账号已锁定,请稍后再试") raise LoginException(data="", message="账号已锁定,请稍后再试")
# 判断是否开启验证码,开启则验证,否则不验证 # 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
if login_user.captcha_enabled: 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) await cls.__check_login_captcha(request, login_user)
user = login_by_account(query_db, login_user.user_name) user = login_by_account(query_db, login_user.user_name)
if not user: if not user:
@ -124,9 +130,9 @@ class LoginService:
if expires_delta: if expires_delta:
expire = datetime.utcnow() + expires_delta expire = datetime.utcnow() + expires_delta
else: else:
expire = datetime.utcnow() + timedelta(minutes=15) expire = datetime.utcnow() + timedelta(minutes=30)
to_encode.update({"exp": expire}) 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 return encoded_jwt
@classmethod @classmethod
@ -146,7 +152,7 @@ class LoginService:
try: try:
if token.startswith('Bearer'): if token.startswith('Bearer'):
token = token.split(' ')[1] 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") user_id: str = payload.get("user_id")
session_id: str = payload.get("session_id") session_id: str = payload.get("session_id")
if user_id is None: 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}") # redis_token = await request.app.state.redis.get(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}")
if token == redis_token: if token == redis_token:
await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", 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, # 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')] role_id_list = [item.role_id for item in query_user.get('user_role_info')]
if 1 in role_id_list: if 1 in role_id_list:
@ -255,63 +261,104 @@ class LoginService:
return router_list 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='两次输入的密码不一致')
async def get_sms_code_services(request: Request, query_db: Session, user: ResetUserModel): return CrudResponseModel(**result)
"""
获取短信验证码service
:param request: Request对象
:param query_db: orm对象
:param user: 用户对象
:return: 短信验证码对象
"""
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))
# 此处模拟调用短信服务
message_service(sms_code)
return SmsCode(**dict(is_success=True, sms_code=sms_code, session_id=session_id, message='获取成功'))
return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='用户不存在')) @classmethod
async def get_sms_code_services(cls, request: Request, query_db: Session, user: ResetUserModel):
"""
获取短信验证码service
:param request: Request对象
:param query_db: orm对象
:param user: 用户对象
:return: 短信验证码对象
"""
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))
# 此处模拟调用短信服务
message_service(sms_code)
return SmsCode(**dict(is_success=True, sms_code=sms_code, session_id=session_id, message='获取成功'))
async def forget_user_services(request: Request, query_db: Session, forget_user: ResetUserModel): return SmsCode(**dict(is_success=False, sms_code='', session_id='', message='用户不存在'))
"""
用户忘记密码services
:param request: Request对象
:param query_db: orm对象
:param forget_user: 重置用户对象
:return: 重置结果
"""
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
edit_result = UserService.reset_user_services(query_db, forget_user)
result = edit_result.dict()
elif not redis_sms_result:
result = dict(is_success=False, message='短信验证码已过期')
else:
await request.app.state.redis.delete(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{forget_user.session_id}")
result = dict(is_success=False, message='短信验证码不正确')
return CrudResponseModel(**result) @classmethod
async def forget_user_services(cls, request: Request, query_db: Session, forget_user: ResetUserModel):
"""
用户忘记密码services
:param request: Request对象
:param query_db: orm对象
:param forget_user: 重置用户对象
:return: 重置结果
"""
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
edit_result = UserService.reset_user_services(query_db, forget_user)
result = edit_result.dict()
elif not redis_sms_result:
result = dict(is_success=False, message='短信验证码已过期')
else:
await request.app.state.redis.delete(f"{RedisInitKeyConfig.SMS_CODE.get('key')}:{forget_user.session_id}")
result = dict(is_success=False, message='短信验证码不正确')
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对象 退出登录services
:param session_id: 会话编号 :param request: Request对象
:return: 退出登录结果 :param session_id: 会话编号
""" :return: 退出登录结果
await request.app.state.redis.delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}") """
# await request.app.state.redis.delete(f'{current_user.user.user_id}_access_token') await request.app.state.redis.delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}")
# await request.app.state.redis.delete(f'{current_user.user.user_id}_session_id') # await request.app.state.redis.delete(f'{current_user.user.user_id}_access_token')
# await request.app.state.redis.delete(f'{current_user.user.user_id}_session_id')
return True return True

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

@ -9,16 +9,17 @@ class NoticeService:
""" """
@classmethod @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 获取通知公告列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 通知公告列表信息对象 :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 @classmethod
def add_notice_services(cls, query_db: Session, page_object: NoticeModel): 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] access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys]
online_info_list = [] online_info_list = []
for item in access_token_values_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( online_dict = dict(
token_id=payload.get('session_id'), token_id=payload.get('session_id'),
user_name=payload.get('user_name'), user_name=payload.get('user_name'),

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

@ -8,16 +8,17 @@ class PostService:
岗位管理模块服务层 岗位管理模块服务层
""" """
@classmethod @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 获取岗位列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 岗位列表信息对象 :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 @classmethod
def add_post_services(cls, query_db: Session, page_object: PostModel): 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.entity.vo.common_vo import CrudResponseModel
from module_admin.dao.user_dao import UserDao from module_admin.dao.user_dao import UserDao
from module_admin.dao.role_dao import * from module_admin.dao.role_dao import *
from utils.page_util import PageResponseModel
from utils.common_util import export_list2excel, CamelCaseUtil from utils.common_util import export_list2excel, CamelCaseUtil
@ -38,16 +39,17 @@ class RoleService:
return result return result
@classmethod @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 获取角色列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param is_page: 是否开启分页
:return: 角色列表信息对象 :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 @classmethod
def add_role_services(cls, query_db: Session, page_object: AddRoleModel): def add_role_services(cls, query_db: Session, page_object: AddRoleModel):
@ -230,27 +232,39 @@ class RoleService:
return binary_data return binary_data
@classmethod @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获取已分配用户列表 根据角色id获取已分配用户列表
:param query_db: orm对象 :param query_db: orm对象
:param page_object: 用户关联角色对象 :param page_object: 用户关联角色对象
:param is_page: 是否开启分页
:return: 已分配用户列表 :return: 已分配用户列表
""" """
query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object) query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object, is_page)
allocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list] allocated_list = PageResponseModel(
**{
**query_user_list.model_dump(by_alias=True),
'rows': [UserInfoModel(**row) for row in query_user_list.rows]
}
)
return allocated_list return allocated_list
@classmethod @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获取未分配用户列表 根据角色id获取未分配用户列表
:param query_db: orm对象 :param query_db: orm对象
:param page_object: 用户关联角色对象 :param page_object: 用户关联角色对象
:param is_page: 是否开启分页
:return: 未分配用户列表 :return: 未分配用户列表
""" """
query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object) query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object, is_page)
unallocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list] unallocated_list = PageResponseModel(
**{
**query_user_list.model_dump(by_alias=True),
'rows': [UserInfoModel(**row) for row in query_user_list.rows]
}
)
return unallocated_list return unallocated_list

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

@ -1,8 +1,9 @@
from fastapi import UploadFile from fastapi import UploadFile
from module_admin.service.role_service import RoleService 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.entity.vo.common_vo import CrudResponseModel
from module_admin.dao.user_dao import * from module_admin.dao.user_dao import *
from utils.page_util import PageResponseModel
from utils.pwd_util import * from utils.pwd_util import *
from utils.common_util import * from utils.common_util import *
@ -13,18 +14,27 @@ class UserService:
""" """
@classmethod @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 获取用户列表信息service
:param query_db: orm对象 :param query_db: orm对象
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:param data_scope_sql: 数据权限对应的查询sql语句 :param data_scope_sql: 数据权限对应的查询sql语句
:param is_page: 是否开启分页
:return: 用户列表信息对象 :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)
user_list_result = [] if is_page:
if query_result: user_list_result = PageResponseModel(
user_list_result = [{**CamelCaseUtil.transform_result(row[0]), 'dept': CamelCaseUtil.transform_result(row[1])} for row in query_result] **{
**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 = [{**row[0], 'dept': row[1]} for row in query_result]
return user_list_result return user_list_result
@ -134,7 +144,7 @@ class UserService:
:param user_id: 用户id :param user_id: 用户id
:return: 用户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) roles = RoleService.get_role_select_option_services(query_db)
if user_id != '': if user_id != '':
query_user = UserDao.get_user_detail_by_id(query_db, user_id=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.styles import Alignment, PatternFill
from openpyxl.utils import get_column_letter from openpyxl.utils import get_column_letter
from openpyxl.worksheet.datavalidation import DataValidation from openpyxl.worksheet.datavalidation import DataValidation
from sqlalchemy.engine.row import Row
from typing import List from typing import List
from config.env import CachePathConfig from config.env import CachePathConfig
@ -66,7 +67,10 @@ class CamelCaseUtil:
return {cls.__to_camel_case(k): v for k, v in result.items()} return {cls.__to_camel_case(k): v for k, v in result.items()}
# 如果是一组字典或其他类型的列表,遍历列表进行转换 # 如果是一组字典或其他类型的列表,遍历列表进行转换
elif isinstance(result, list): 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: else:
return cls.transform_result({c.name: getattr(result, c.name) for c in result.__table__.columns}) return cls.transform_result({c.name: getattr(result, c.name) for c in result.__table__.columns})

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

@ -1,30 +1,9 @@
import math import math
from typing import Optional, List from typing import Optional, List
from sqlalchemy.orm.query import Query
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from pydantic.alias_generators import to_camel from pydantic.alias_generators import to_camel
from utils.common_util import CamelCaseUtil
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
class PageResponseModel(BaseModel): class PageResponseModel(BaseModel):
@ -40,33 +19,64 @@ class PageResponseModel(BaseModel):
has_next: Optional[bool] = None has_next: Optional[bool] = None
def get_page_info(offset: int, page_num: int, page_size: int, count: int): class PageUtil:
""" """
根据分页参数获取分页信息 分页工具类
:param offset: 起始数据位置
:param page_num: 当前页码
:param page_size: 当前页面数据量
:param count: 数据总数
: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
result = dict(offset=res_offset, page_num=res_page_num, page_size=page_size, total=count, has_next=has_next) @classmethod
def get_page_obj(cls, data_list: List, page_num: int, page_size: int):
"""
输入数据列表data_list和分页信息返回分页数据列表结果
:param data_list: 原始数据列表
:param page_num: 当前页码
:param page_size: 当前页面数据量
:return: 分页数据对象
"""
# 计算起始索引和结束索引
start = (page_num - 1) * page_size
end = page_num * page_size
# 根据计算得到的起始索引和结束索引对数据列表进行切片
paginated_data = data_list[start:end]
has_next = True if math.ceil(len(data_list) / page_size) > page_num else False
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 PageModel(**result) return result
def get_page_obj(data_list: List, page_num: int, page_size: int): 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 return result

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

@ -187,115 +187,3 @@ class ResponseUtil:
status_code=status.HTTP_200_OK, status_code=status.HTTP_200_OK,
content=data 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" "url": "https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@antv/g2plot": "^2.4.31",
"@element-plus/icons-vue": "2.3.1", "@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1", "@vueuse/core": "10.6.1",
"ant-design-vue": "^4.1.1",
"axios": "0.27.2", "axios": "0.27.2",
"echarts": "5.4.3", "echarts": "5.4.3",
"element-plus": "2.4.3", "element-plus": "2.4.3",
@ -35,11 +38,12 @@
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "4.5.0", "@vitejs/plugin-vue": "4.5.0",
"@vue/compiler-sfc": "3.3.9", "@vue/compiler-sfc": "3.3.9",
"less": "^4.2.0",
"sass": "1.69.5", "sass": "1.69.5",
"unplugin-auto-import": "0.17.1", "unplugin-auto-import": "0.17.1",
"unplugin-vue-setup-extend-plus": "1.0.0",
"vite": "5.0.4", "vite": "5.0.4",
"vite-plugin-compression": "0.5.1", "vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1", "vite-plugin-svg-icons": "2.0.1"
"unplugin-vue-setup-extend-plus": "1.0.0"
} }
} }

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

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

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

@ -64,7 +64,7 @@ export const constantRoutes = [
children: [ children: [
{ {
path: '/index', path: '/index',
component: () => import('@/views/index'), component: () => import('@/views/dashboard/index'),
name: 'Index', name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true } 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 { proxy } = getCurrentInstance();
const loginForm = ref({ const loginForm = ref({
username: "admin", username: "",
password: "admin123", password: "",
rememberMe: false, rememberMe: false,
code: "", code: "",
uuid: "" uuid: ""
@ -140,6 +140,7 @@ function handleLogin() {
function getCode() { function getCode() {
getCodeImg().then(res => { getCodeImg().then(res => {
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled; captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
register.value = res.registerEnabled === undefined ? false : res.registerEnabled;
if (captchaEnabled.value) { if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img; codeUrl.value = "data:image/gif;base64," + res.img;
loginForm.value.uuid = res.uuid; loginForm.value.uuid = res.uuid;

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

@ -1,7 +1,7 @@
<template> <template>
<div class="register"> <div class="register">
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form"> <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-form-item prop="username">
<el-input <el-input
v-model="registerForm.username" v-model="registerForm.username"
@ -70,7 +70,7 @@
</el-form> </el-form>
<!-- 底部 --> <!-- 底部 -->
<div class="el-register-footer"> <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>
</div> </div>
</template> </template>

Loading…
Cancel
Save