!1 RuoYi-Vue3-FastAPI v1.0.0
Merge pull request !1 from insistence/develop
This commit is contained in:
36
README.en.md
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
199
README.md
@@ -1,39 +1,184 @@
|
||||
# RuoYi-Vue3-FastAPI
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.0.0</h1>
|
||||
<h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/stargazers"><img src="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://github.com/insistence/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/github/stars/insistence/RuoYi-Vue3-FastAPI?style=social"></a>
|
||||
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/badge/RuoYiVue3FastAPI-v1.0.0-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
<img src="https://img.shields.io/badge/python-≥3.8-blue">
|
||||
<img src="https://img.shields.io/badge/MySQL-≥5.7-blue">
|
||||
</p>
|
||||
|
||||
#### 介绍
|
||||
{**以下是 Gitee 平台说明,您可以替换此简介**
|
||||
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
|
||||
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
|
||||
## 平台简介
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
RuoYi-Vue-FastAPI是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
|
||||
|
||||
* 前端采用Vue、Element Plus,基于<u>[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3)</u>前端项目修改。
|
||||
* 后端采用FastAPI、sqlalchemy、MySQL、Redis、OAuth2 & Jwt。
|
||||
* 权限认证使用OAuth2 & Jwt,支持多终端认证系统。
|
||||
* 支持加载动态权限菜单,多方式轻松权限控制。
|
||||
* Vue2版本:
|
||||
- Gitte仓库地址:https://gitee.com/insistence2022/RuoYi-Vue-FastAPI。
|
||||
- GitHub仓库地址:https://github.com/insistence/RuoYi-Vue-FastAPI。
|
||||
* 纯Python版本:
|
||||
- Gitte仓库地址:https://gitee.com/insistence2022/dash-fastapi-admin。
|
||||
- GitHub仓库地址:https://github.com/insistence/Dash-FastAPI-Admin。
|
||||
* 特别鸣谢:<u>[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3)</u>。
|
||||
|
||||
#### 安装教程
|
||||
## 内置功能
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
2. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
|
||||
3. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||
4. 部门管理:配置系统组织机构(公司、部门、小组)。
|
||||
5. 岗位管理:配置系统用户所属担任职务。
|
||||
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
||||
7. 参数管理:对系统动态配置常用参数。
|
||||
8. 通知公告:系统通知公告信息发布维护。
|
||||
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
10. 登录日志:系统登录日志记录查询包含登录异常。
|
||||
11. 在线用户:当前系统中活跃用户状态监控。
|
||||
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
|
||||
13. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
|
||||
14. 缓存监控:对系统的缓存信息查询,命令统计等。
|
||||
15. 系统接口:根据业务代码自动生成相关的api接口文档。
|
||||
|
||||
#### 使用说明
|
||||
## 演示图
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/login.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dashboard.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/user.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/role.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/menu.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dept.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/post.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/dict.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/config.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/notice.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/operLog.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/loginLog.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/online.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/job.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/server.png"/></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/cache.png"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/cacheList.png"></td>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/api.png"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/profile.png"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#### 参与贡献
|
||||
## 在线体验
|
||||
- *账号:admin*
|
||||
- *密码:admin123*
|
||||
- 演示地址:<a href="https://vfadmin.insistence.tech">vfadmin管理系统<a>
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
## 项目开发及发布相关
|
||||
|
||||
### 开发
|
||||
|
||||
#### 特技
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
# 进入项目根目录
|
||||
cd RuoYi-Vue3-FastAPI
|
||||
```
|
||||
|
||||
#### 前端
|
||||
```bash
|
||||
# 进入前端目录
|
||||
cd ruoyi-fastapi-frontend
|
||||
|
||||
# 安装依赖
|
||||
npm install 或 yarn --registry=https://registry.npmmirror.com
|
||||
|
||||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动服务
|
||||
npm run dev 或 yarn dev
|
||||
```
|
||||
|
||||
#### 后端
|
||||
```bash
|
||||
# 进入后端目录
|
||||
cd ruoyi-fastapi-backend
|
||||
|
||||
# 安装项目依赖环境
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
# 配置环境
|
||||
在.env.dev文件中配置开发环境的数据库和redis
|
||||
|
||||
# 运行sql文件
|
||||
1.新建数据库ruoyi-fastapi(默认,可修改)
|
||||
2.使用命令或数据库连接工具运行sql文件夹下的ruoyi-fastapi.sql
|
||||
|
||||
# 运行后端
|
||||
python3 app.py --env=dev
|
||||
```
|
||||
|
||||
#### 访问
|
||||
```bash
|
||||
# 默认账号密码
|
||||
账号:admin
|
||||
密码:admin123
|
||||
|
||||
# 浏览器访问
|
||||
地址:http://localhost:80
|
||||
```
|
||||
|
||||
### 发布
|
||||
|
||||
#### 前端
|
||||
```bash
|
||||
# 构建测试环境
|
||||
npm run build:stage 或 yarn build:stage
|
||||
|
||||
# 构建生产环境
|
||||
npm run build:prod 或 yarn build:prod
|
||||
```
|
||||
|
||||
#### 后端
|
||||
```bash
|
||||
# 配置环境
|
||||
在.env.prod文件中配置生产环境的数据库和redis
|
||||
|
||||
# 运行后端
|
||||
python3 app.py --env=prod
|
||||
```
|
||||
|
||||
## 交流与赞助
|
||||
如果有对本项目及FastAPI感兴趣的朋友,欢迎加入知识星球一起交流学习,让我们一起变得更强。如果你觉得这个项目帮助到了你,你可以请作者喝杯咖啡表示鼓励☕。扫描下面微信二维码添加微信备注VF-Admin即可进群。
|
||||
<table>
|
||||
<tr>
|
||||
<td><img alt="zsxq" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/zsxq.jpg"></td>
|
||||
<td><img alt="zanzhu" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/zanzhu.jpg"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img alt="wxcode" src="https://gitee.com/insistence2022/RuoYi-Vue-FastAPI/raw/master/demo-pictures/wxcode.jpg"></td>
|
||||
</tr>
|
||||
</table>
|
50
ruoyi-fastapi-backend/.env.dev
Normal file
50
ruoyi-fastapi-backend/.env.dev
Normal file
@@ -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
Normal file
50
ruoyi-fastapi-backend/.env.prod
Normal file
@@ -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
|
@@ -1,119 +1,12 @@
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
import uvicorn
|
||||
from contextlib import asynccontextmanager
|
||||
from module_admin.controller.login_controller import loginController
|
||||
from module_admin.controller.captcha_controller import captchaController
|
||||
from module_admin.controller.user_controller import userController
|
||||
from module_admin.controller.menu_controller import menuController
|
||||
from module_admin.controller.dept_controller import deptController
|
||||
from module_admin.controller.role_controller import roleController
|
||||
from module_admin.controller.post_controler import postController
|
||||
from module_admin.controller.dict_controller import dictController
|
||||
from module_admin.controller.config_controller import configController
|
||||
from module_admin.controller.notice_controller import noticeController
|
||||
from module_admin.controller.log_controller import logController
|
||||
from module_admin.controller.online_controller import onlineController
|
||||
from module_admin.controller.job_controller import jobController
|
||||
from module_admin.controller.server_controller import serverController
|
||||
from module_admin.controller.cache_controller import cacheController
|
||||
from module_admin.controller.common_controller import commonController
|
||||
from config.env import UploadConfig
|
||||
from config.get_redis import RedisUtil
|
||||
from config.get_db import init_create_table
|
||||
from config.get_scheduler import SchedulerUtil
|
||||
from utils.response_util import *
|
||||
from utils.log_util import logger
|
||||
from utils.common_util import worship
|
||||
from server import app, AppConfig
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
logger.info("RuoYi-FastAPI开始启动")
|
||||
worship()
|
||||
await init_create_table()
|
||||
app.state.redis = await RedisUtil.create_redis_pool()
|
||||
await RedisUtil.init_sys_dict(app.state.redis)
|
||||
await RedisUtil.init_sys_config(app.state.redis)
|
||||
await SchedulerUtil.init_system_scheduler()
|
||||
logger.info("RuoYi-FastAPI启动成功")
|
||||
yield
|
||||
await RedisUtil.close_redis_pool(app)
|
||||
await SchedulerUtil.close_system_scheduler()
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title='RuoYi-FastAPI',
|
||||
description='RuoYi-FastAPI接口文档',
|
||||
version='1.0.0',
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# 前端页面url
|
||||
origins = [
|
||||
"http://localhost:81",
|
||||
"http://127.0.0.1:81",
|
||||
]
|
||||
|
||||
# 后台api允许跨域
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 实例化UploadConfig,确保应用启动时上传目录存在
|
||||
upload_config = UploadConfig()
|
||||
|
||||
# 挂载静态文件路径
|
||||
app.mount(f"{upload_config.UPLOAD_PREFIX}", StaticFiles(directory=f"{upload_config.UPLOAD_PATH}"), name="profile")
|
||||
|
||||
|
||||
# 自定义token检验异常
|
||||
@app.exception_handler(AuthException)
|
||||
async def auth_exception_handler(request: Request, exc: AuthException):
|
||||
return ResponseUtil.unauthorized(data=exc.data, msg=exc.message)
|
||||
|
||||
|
||||
# 自定义权限检验异常
|
||||
@app.exception_handler(PermissionException)
|
||||
async def permission_exception_handler(request: Request, exc: PermissionException):
|
||||
return ResponseUtil.forbidden(data=exc.data, msg=exc.message)
|
||||
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(request: Request, exc: HTTPException):
|
||||
return JSONResponse(
|
||||
content=jsonable_encoder({"message": exc.detail, "code": exc.status_code}),
|
||||
status_code=exc.status_code
|
||||
)
|
||||
|
||||
|
||||
controller_list = [
|
||||
{'router': loginController, 'tags': ['登录模块']},
|
||||
{'router': captchaController, 'tags': ['验证码模块']},
|
||||
{'router': userController, 'tags': ['系统管理-用户管理']},
|
||||
{'router': roleController, 'tags': ['系统管理-角色管理']},
|
||||
{'router': menuController, 'tags': ['系统管理-菜单管理']},
|
||||
{'router': deptController, 'tags': ['系统管理-部门管理']},
|
||||
{'router': postController, 'tags': ['系统管理-岗位管理']},
|
||||
{'router': dictController, 'tags': ['系统管理-字典管理']},
|
||||
{'router': configController, 'tags': ['系统管理-参数管理']},
|
||||
{'router': noticeController, 'tags': ['系统管理-通知公告管理']},
|
||||
{'router': logController, 'tags': ['系统管理-日志管理']},
|
||||
{'router': onlineController, 'tags': ['系统监控-在线用户']},
|
||||
{'router': jobController, 'tags': ['系统监控-定时任务']},
|
||||
{'router': serverController, 'tags': ['系统监控-菜单管理']},
|
||||
{'router': cacheController, 'tags': ['系统监控-缓存监控']},
|
||||
{'router': commonController, 'tags': ['通用模块']}
|
||||
]
|
||||
|
||||
for controller in controller_list:
|
||||
app.include_router(router=controller.get('router'), tags=controller.get('tags'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
uvicorn.run(app='app:app', host="0.0.0.0", port=9099, root_path='/dev-api', reload=True)
|
||||
uvicorn.run(
|
||||
app='app:app',
|
||||
host=AppConfig.app_host,
|
||||
port=AppConfig.app_port,
|
||||
root_path=AppConfig.app_root_path,
|
||||
reload=AppConfig.app_reload
|
||||
)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 79 KiB |
@@ -4,8 +4,8 @@ from sqlalchemy.orm import sessionmaker
|
||||
from urllib.parse import quote_plus
|
||||
from config.env import DataBaseConfig
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.USERNAME}:{quote_plus(DataBaseConfig.PASSWORD)}@" \
|
||||
f"{DataBaseConfig.HOST}:{DataBaseConfig.PORT}/{DataBaseConfig.DB}"
|
||||
SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{DataBaseConfig.db_username}:{quote_plus(DataBaseConfig.db_password)}@" \
|
||||
f"{DataBaseConfig.db_host}:{DataBaseConfig.db_port}/{DataBaseConfig.db_database}"
|
||||
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URL, echo=True
|
||||
|
@@ -1,39 +1,57 @@
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from pydantic_settings import BaseSettings
|
||||
from functools import lru_cache
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
class JwtConfig:
|
||||
class AppSettings(BaseSettings):
|
||||
"""
|
||||
应用配置
|
||||
"""
|
||||
app_env: str = 'dev'
|
||||
app_name: str = 'RuoYi-FasAPI'
|
||||
app_root_path: str = '/dev-api'
|
||||
app_host: str = '0.0.0.0'
|
||||
app_port: int = 9099
|
||||
app_version: str = '1.0.0'
|
||||
app_reload: bool = True
|
||||
|
||||
|
||||
class JwtSettings(BaseSettings):
|
||||
"""
|
||||
Jwt配置
|
||||
"""
|
||||
SECRET_KEY = "b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 1440
|
||||
REDIS_TOKEN_EXPIRE_MINUTES = 30
|
||||
jwt_secret_key: str = 'b01c66dc2c58dc6a0aabfe2144256be36226de378bf87f72c0c795dda67f4d55'
|
||||
jwt_algorithm: str = 'HS256'
|
||||
jwt_expire_minutes: int = 1440
|
||||
jwt_redis_expire_minutes: int = 30
|
||||
|
||||
|
||||
class DataBaseConfig:
|
||||
class DataBaseSettings(BaseSettings):
|
||||
"""
|
||||
数据库配置
|
||||
"""
|
||||
HOST = "127.0.0.1"
|
||||
PORT = 3306
|
||||
USERNAME = 'root'
|
||||
PASSWORD = 'mysqlroot'
|
||||
DB = 'ruoyi-fastapi'
|
||||
db_host: str = '127.0.0.1'
|
||||
db_port: int = 3306
|
||||
db_username: str = 'root'
|
||||
db_password: str = 'mysqlroot'
|
||||
db_database: str = 'ruoyi-fastapi'
|
||||
|
||||
|
||||
class RedisConfig:
|
||||
class RedisSettings(BaseSettings):
|
||||
"""
|
||||
Redis配置
|
||||
"""
|
||||
HOST = "127.0.0.1"
|
||||
PORT = 6379
|
||||
USERNAME = ''
|
||||
PASSWORD = ''
|
||||
DB = 2
|
||||
redis_host: str = '127.0.0.1'
|
||||
redis_port: int = 6379
|
||||
redis_username: str = ''
|
||||
redis_password: str = ''
|
||||
redis_database: int = 2
|
||||
|
||||
|
||||
class UploadConfig:
|
||||
class UploadSettings:
|
||||
"""
|
||||
上传配置
|
||||
"""
|
||||
@@ -80,3 +98,92 @@ class RedisInitKeyConfig:
|
||||
ACCOUNT_LOCK = {'key': 'account_lock', 'remark': '用户锁定'}
|
||||
PASSWORD_ERROR_COUNT = {'key': 'password_error_count', 'remark': '密码错误次数'}
|
||||
SMS_CODE = {'key': 'sms_code', 'remark': '短信验证码'}
|
||||
|
||||
|
||||
class GetConfig:
|
||||
"""
|
||||
获取配置
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.parse_cli_args()
|
||||
|
||||
@lru_cache()
|
||||
def get_app_config(self):
|
||||
"""
|
||||
获取应用配置
|
||||
"""
|
||||
# 实例化应用配置模型
|
||||
return AppSettings()
|
||||
|
||||
@lru_cache()
|
||||
def get_jwt_config(self):
|
||||
"""
|
||||
获取Jwt配置
|
||||
"""
|
||||
# 实例化Jwt配置模型
|
||||
return JwtSettings()
|
||||
|
||||
@lru_cache()
|
||||
def get_database_config(self):
|
||||
"""
|
||||
获取数据库配置
|
||||
"""
|
||||
# 实例化数据库配置模型
|
||||
return DataBaseSettings()
|
||||
|
||||
@lru_cache()
|
||||
def get_redis_config(self):
|
||||
"""
|
||||
获取Redis配置
|
||||
"""
|
||||
# 实例化Redis配置模型
|
||||
return RedisSettings()
|
||||
|
||||
@lru_cache()
|
||||
def get_upload_config(self):
|
||||
"""
|
||||
获取数据库配置
|
||||
"""
|
||||
# 实例上传配置
|
||||
return UploadSettings()
|
||||
|
||||
@staticmethod
|
||||
def parse_cli_args():
|
||||
"""
|
||||
解析命令行参数
|
||||
"""
|
||||
if 'uvicorn' in sys.argv[0]:
|
||||
# 使用uvicorn启动时,命令行参数需要按照uvicorn的文档进行配置,无法自定义参数
|
||||
pass
|
||||
else:
|
||||
# 使用argparse定义命令行参数
|
||||
parser = argparse.ArgumentParser(description='命令行参数')
|
||||
parser.add_argument('--env', type=str, default='', help='运行环境')
|
||||
# 解析命令行参数
|
||||
args = parser.parse_args()
|
||||
# 设置环境变量,如果未设置命令行参数,默认APP_ENV为dev
|
||||
os.environ['APP_ENV'] = args.env if args.env else 'dev'
|
||||
# 读取运行环境
|
||||
run_env = os.environ.get('APP_ENV', '')
|
||||
# 运行环境未指定时默认加载.env.dev
|
||||
env_file = '.env.dev'
|
||||
# 运行环境不为空时按命令行参数加载对应.env文件
|
||||
if run_env != '':
|
||||
env_file = f'.env.{run_env}'
|
||||
# 加载配置
|
||||
load_dotenv(env_file)
|
||||
|
||||
|
||||
# 实例化获取配置类
|
||||
get_config = GetConfig()
|
||||
# 应用配置
|
||||
AppConfig = get_config.get_app_config()
|
||||
# Jwt配置
|
||||
JwtConfig = get_config.get_jwt_config()
|
||||
# 数据库配置
|
||||
DataBaseConfig = get_config.get_database_config()
|
||||
# Redis配置
|
||||
RedisConfig = get_config.get_redis_config()
|
||||
# 上传配置
|
||||
UploadConfig = get_config.get_upload_config()
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import aioredis
|
||||
from redis import asyncio as aioredis
|
||||
from redis.exceptions import AuthenticationError, TimeoutError, RedisError
|
||||
from module_admin.service.dict_service import DictDataService
|
||||
from module_admin.service.config_service import ConfigService
|
||||
from config.env import RedisConfig
|
||||
@@ -19,15 +20,26 @@ class RedisUtil:
|
||||
"""
|
||||
logger.info("开始连接redis...")
|
||||
redis = await aioredis.from_url(
|
||||
url=f"redis://{RedisConfig.HOST}",
|
||||
port=RedisConfig.PORT,
|
||||
username=RedisConfig.USERNAME,
|
||||
password=RedisConfig.PASSWORD,
|
||||
db=RedisConfig.DB,
|
||||
url=f"redis://{RedisConfig.redis_host}",
|
||||
port=RedisConfig.redis_port,
|
||||
username=RedisConfig.redis_username,
|
||||
password=RedisConfig.redis_password,
|
||||
db=RedisConfig.redis_database,
|
||||
encoding="utf-8",
|
||||
decode_responses=True
|
||||
)
|
||||
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
|
||||
|
||||
@classmethod
|
||||
|
@@ -70,11 +70,11 @@ job_stores = {
|
||||
'sqlalchemy': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URL, engine=engine),
|
||||
'redis': RedisJobStore(
|
||||
**dict(
|
||||
host=RedisConfig.HOST,
|
||||
port=RedisConfig.PORT,
|
||||
username=RedisConfig.USERNAME,
|
||||
password=RedisConfig.PASSWORD,
|
||||
db=RedisConfig.DB
|
||||
host=RedisConfig.redis_host,
|
||||
port=RedisConfig.redis_port,
|
||||
username=RedisConfig.redis_username,
|
||||
password=RedisConfig.redis_password,
|
||||
db=RedisConfig.redis_database
|
||||
)
|
||||
)
|
||||
}
|
||||
|
28
ruoyi-fastapi-backend/exceptions/exception.py
Normal file
28
ruoyi-fastapi-backend/exceptions/exception.py
Normal file
@@ -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
Normal file
27
ruoyi-fastapi-backend/exceptions/handle.py
Normal file
@@ -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
Normal file
19
ruoyi-fastapi-backend/middlewares/cors_middleware.py
Normal file
@@ -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
Normal file
10
ruoyi-fastapi-backend/middlewares/handle.py
Normal file
@@ -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)
|
@@ -1,7 +1,7 @@
|
||||
from fastapi import Depends
|
||||
from module_admin.entity.vo.user_vo import CurrentUserModel
|
||||
from module_admin.service.login_service import LoginService
|
||||
from utils.response_util import PermissionException
|
||||
from exceptions.exception import PermissionException
|
||||
|
||||
|
||||
class CheckUserInterfaceAuth:
|
||||
|
@@ -15,6 +15,8 @@ captchaController = APIRouter()
|
||||
async def get_captcha_image(request: Request):
|
||||
try:
|
||||
captcha_enabled = True if await request.app.state.redis.get(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == 'true' else False
|
||||
register_enabled = True if await request.app.state.redis.get(
|
||||
f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.registerUser") == 'true' else False
|
||||
session_id = str(uuid.uuid4())
|
||||
captcha_result = CaptchaService.create_captcha_image_service()
|
||||
image = captcha_result[0]
|
||||
@@ -22,7 +24,7 @@ async def get_captcha_image(request: Request):
|
||||
await request.app.state.redis.set(f"{RedisInitKeyConfig.CAPTCHA_CODES.get('key')}:{session_id}", computed_result, ex=timedelta(minutes=2))
|
||||
logger.info(f'编号为{session_id}的会话获取图片验证码成功')
|
||||
return ResponseUtil.success(
|
||||
model_content=CaptchaCode(captchaEnabled=captcha_enabled, img=image, uuid=session_id)
|
||||
model_content=CaptchaCode(captchaEnabled=captcha_enabled, registerEnabled=register_enabled, img=image, uuid=session_id)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
|
@@ -17,11 +17,8 @@ configController = APIRouter(prefix='/system/config', dependencies=[Depends(Logi
|
||||
@configController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:config:list'))])
|
||||
async def get_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
config_query_result = ConfigService.get_config_list_services(query_db, config_query)
|
||||
# 分页操作
|
||||
config_page_query_result = get_page_obj(config_query_result, config_page_query.page_num, config_page_query.page_size)
|
||||
# 获取分页数据
|
||||
config_page_query_result = ConfigService.get_config_list_services(query_db, config_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=config_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -125,9 +122,8 @@ async def query_system_config(request: Request, config_key: str):
|
||||
@log_decorator(title='参数管理', business_type=5)
|
||||
async def export_system_config_list(request: Request, config_page_query: ConfigPageQueryModel = Depends(ConfigPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
config_query = ConfigQueryModel(**config_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
config_query_result = ConfigService.get_config_list_services(query_db, config_query)
|
||||
config_query_result = ConfigService.get_config_list_services(query_db, config_page_query, is_page=False)
|
||||
config_export_result = ConfigService.export_config_list_services(config_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(config_export_result))
|
||||
|
@@ -5,7 +5,7 @@ from module_admin.service.login_service import LoginService, CurrentUserModel
|
||||
from module_admin.service.dict_service import *
|
||||
from utils.response_util import *
|
||||
from utils.log_util import *
|
||||
from utils.page_util import *
|
||||
from utils.page_util import PageResponseModel
|
||||
from utils.common_util import bytes2file_response
|
||||
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
|
||||
from module_admin.annotation.log_annotation import log_decorator
|
||||
@@ -17,11 +17,8 @@ dictController = APIRouter(prefix='/system/dict', dependencies=[Depends(LoginSer
|
||||
@dictController.get("/type/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
|
||||
async def get_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query)
|
||||
# 分页操作
|
||||
dict_type_page_query_result = get_page_obj(dict_type_query_result, dict_type_page_query.page_num, dict_type_page_query.page_size)
|
||||
# 获取分页数据
|
||||
dict_type_page_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=dict_type_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -101,7 +98,7 @@ async def delete_system_dict_type(request: Request, dict_ids: str, query_db: Ses
|
||||
@dictController.get("/type/optionselect", response_model=List[DictTypeModel], dependencies=[Depends(CheckUserInterfaceAuth('system:dict:query'))])
|
||||
async def query_system_dict_type_options(request: Request, query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, DictTypeQueryModel(**dict()))
|
||||
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, DictTypePageQueryModel(**dict()), is_page=False)
|
||||
logger.info(f'获取成功')
|
||||
return ResponseUtil.success(data=dict_type_query_result)
|
||||
except Exception as e:
|
||||
@@ -124,9 +121,8 @@ async def query_detail_system_dict_type(request: Request, dict_id: int, query_db
|
||||
@log_decorator(title='字典管理', business_type=5)
|
||||
async def export_system_dict_type_list(request: Request, dict_type_page_query: DictTypePageQueryModel = Depends(DictTypePageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
dict_type_query = DictTypeQueryModel(**dict_type_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_query)
|
||||
dict_type_query_result = DictTypeService.get_dict_type_list_services(query_db, dict_type_page_query, is_page=False)
|
||||
dict_type_export_result = DictTypeService.export_dict_type_list_services(dict_type_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(dict_type_export_result))
|
||||
@@ -150,11 +146,8 @@ async def query_system_dict_type_data(request: Request, dict_type: str, query_db
|
||||
@dictController.get("/data/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:dict:list'))])
|
||||
async def get_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query)
|
||||
# 分页操作
|
||||
dict_data_page_query_result = get_page_obj(dict_data_query_result, dict_data_page_query.page_num, dict_data_page_query.page_size)
|
||||
# 获取分页数据
|
||||
dict_data_page_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=dict_data_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -230,9 +223,8 @@ async def query_detail_system_dict_data(request: Request, dict_code: int, query_
|
||||
@log_decorator(title='字典管理', business_type=5)
|
||||
async def export_system_dict_data_list(request: Request, dict_data_page_query: DictDataPageQueryModel = Depends(DictDataPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
dict_data_query = DictDataModel(**dict_data_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_query)
|
||||
dict_data_query_result = DictDataService.get_dict_data_list_services(query_db, dict_data_page_query, is_page=False)
|
||||
dict_data_export_result = DictDataService.export_dict_data_list_services(dict_data_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(dict_data_export_result))
|
||||
|
@@ -18,11 +18,8 @@ jobController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.
|
||||
@jobController.get("/job/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))])
|
||||
async def get_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
job_query = JobModel(**job_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
job_query_result = JobService.get_job_list_services(query_db, job_query)
|
||||
# 分页操作
|
||||
notice_page_query_result = get_page_obj(job_query_result, job_page_query.page_num, job_page_query.page_size)
|
||||
# 获取分页数据
|
||||
notice_page_query_result = JobService.get_job_list_services(query_db, job_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=notice_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -133,9 +130,8 @@ async def query_detail_system_job(request: Request, job_id: int, query_db: Sessi
|
||||
@log_decorator(title='定时任务管理', business_type=5)
|
||||
async def export_system_job_list(request: Request, job_page_query: JobPageQueryModel = Depends(JobPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
job_query = JobModel(**job_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
job_query_result = JobService.get_job_list_services(query_db, job_query)
|
||||
job_query_result = JobService.get_job_list_services(query_db, job_page_query, is_page=False)
|
||||
job_export_result = await JobService.export_job_list_services(request, job_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(job_export_result))
|
||||
@@ -147,13 +143,10 @@ async def export_system_job_list(request: Request, job_page_query: JobPageQueryM
|
||||
@jobController.get("/jobLog/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:job:list'))])
|
||||
async def get_system_job_log_list(request: Request, job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query)
|
||||
# 分页操作
|
||||
notice_page_query_result = get_page_obj(job_log_query_result, job_log_page_query.page_num, job_log_page_query.page_size)
|
||||
# 获取分页数据
|
||||
job_log_page_query_result = JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=notice_page_query_result)
|
||||
return ResponseUtil.success(model_content=job_log_page_query_result)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return ResponseUtil.error(msg=str(e))
|
||||
@@ -196,10 +189,9 @@ async def clear_system_job_log(request: Request, query_db: Session = Depends(get
|
||||
@log_decorator(title='定时任务日志管理', business_type=5)
|
||||
async def export_system_job_log_list(request: Request, job_log_page_query: JobLogPageQueryModel = Depends(JobLogPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
job_log_query = JobLogQueryModel(**job_log_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_query)
|
||||
job_log_export_result = JobLogService.export_job_log_list_services(query_db, job_log_query_result)
|
||||
job_log_query_result = JobLogService.get_job_log_list_services(query_db, job_log_page_query, is_page=False)
|
||||
job_log_export_result = await JobLogService.export_job_log_list_services(request, job_log_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(job_log_export_result))
|
||||
except Exception as e:
|
||||
|
@@ -17,11 +17,8 @@ logController = APIRouter(prefix='/monitor', dependencies=[Depends(LoginService.
|
||||
@logController.get("/operlog/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:operlog:list'))])
|
||||
async def get_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query)
|
||||
# 分页操作
|
||||
operation_log_page_query_result = get_page_obj(operation_log_query_result, operation_log_page_query.page_num, operation_log_page_query.page_size)
|
||||
# 获取分页数据
|
||||
operation_log_page_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=operation_log_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -66,9 +63,8 @@ async def delete_system_operation_log(request: Request, oper_ids: str, query_db:
|
||||
@log_decorator(title='操作日志管理', business_type=5)
|
||||
async def export_system_operation_log_list(request: Request, operation_log_page_query: OperLogPageQueryModel = Depends(OperLogPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
operation_log_query = OperLogQueryModel(**operation_log_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_query)
|
||||
operation_log_query_result = OperationLogService.get_operation_log_list_services(query_db, operation_log_page_query, is_page=False)
|
||||
operation_log_export_result = await OperationLogService.export_operation_log_list_services(request, operation_log_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(operation_log_export_result))
|
||||
@@ -80,11 +76,8 @@ async def export_system_operation_log_list(request: Request, operation_log_page_
|
||||
@logController.get("/logininfor/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('monitor:logininfor:list'))])
|
||||
async def get_system_login_log_list(request: Request, login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query)
|
||||
# 分页操作
|
||||
login_log_page_query_result = get_page_obj(login_log_query_result, login_log_page_query.page_num, login_log_page_query.page_size)
|
||||
# 获取分页数据
|
||||
login_log_page_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=login_log_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -146,9 +139,8 @@ async def clear_system_login_log(request: Request, user_name: str, query_db: Ses
|
||||
@log_decorator(title='登录日志管理', business_type=5)
|
||||
async def export_system_login_log_list(request: Request, login_log_page_query: LoginLogPageQueryModel = Depends(LoginLogPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
login_log_query = LoginLogQueryModel(**login_log_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_query)
|
||||
login_log_query_result = LoginLogService.get_login_log_list_services(query_db, login_log_page_query, is_page=False)
|
||||
login_log_export_result = LoginLogService.export_login_log_list_services(login_log_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(login_log_export_result))
|
||||
|
@@ -2,10 +2,10 @@ from fastapi import APIRouter
|
||||
from module_admin.service.login_service import *
|
||||
from module_admin.entity.vo.login_vo import *
|
||||
from module_admin.dao.login_dao import *
|
||||
from config.env import JwtConfig, RedisInitKeyConfig
|
||||
from utils.response_util import *
|
||||
from utils.log_util import *
|
||||
from module_admin.annotation.log_annotation import log_decorator
|
||||
from config.env import JwtConfig, RedisInitKeyConfig
|
||||
from utils.response_util import ResponseUtil
|
||||
from utils.log_util import *
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D
|
||||
except LoginException as e:
|
||||
return ResponseUtil.failure(msg=e.message)
|
||||
try:
|
||||
access_token_expires = timedelta(minutes=JwtConfig.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token_expires = timedelta(minutes=JwtConfig.jwt_expire_minutes)
|
||||
session_id = str(uuid.uuid4())
|
||||
access_token = LoginService.create_access_token(
|
||||
data={
|
||||
@@ -42,10 +42,11 @@ async def login(request: Request, form_data: CustomOAuth2PasswordRequestForm = D
|
||||
expires_delta=access_token_expires
|
||||
)
|
||||
await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", access_token,
|
||||
ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
|
||||
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
|
||||
# 此方法可实现同一账号同一时间只能登录一次
|
||||
# await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{result[0].user_id}", access_token,
|
||||
# ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
|
||||
# ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
|
||||
UserService.edit_user_services(query_db, EditUserModel(userId=result[0].user_id, loginDate=datetime.now(), type='status'))
|
||||
logger.info('登录成功')
|
||||
# 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
|
||||
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
|
||||
@@ -82,42 +83,57 @@ async def get_login_user_routers(request: Request, current_user: CurrentUserMode
|
||||
return ResponseUtil.error(msg=str(e))
|
||||
|
||||
|
||||
@loginController.post("/getSmsCode", response_model=SmsCode)
|
||||
async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)):
|
||||
@loginController.post("/register", response_model=CrudResponseModel)
|
||||
async def register_user(request: Request, user_register: UserRegister, query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
sms_result = await get_sms_code_services(request, query_db, user)
|
||||
if sms_result.is_success:
|
||||
logger.info('获取成功')
|
||||
return response_200(data=sms_result, message='获取成功')
|
||||
user_register_result = await LoginService.register_user_services(request, query_db, user_register)
|
||||
if user_register_result.is_success:
|
||||
logger.info(user_register_result.message)
|
||||
return ResponseUtil.success(data=user_register_result, msg=user_register_result.message)
|
||||
else:
|
||||
logger.warning(sms_result.message)
|
||||
return response_400(data='', message=sms_result.message)
|
||||
logger.warning(user_register_result.message)
|
||||
return ResponseUtil.failure(msg=user_register_result.message)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return response_500(data="", message=str(e))
|
||||
return ResponseUtil.error(msg=str(e))
|
||||
|
||||
|
||||
@loginController.post("/forgetPwd", response_model=CrudResponseModel)
|
||||
async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
forget_user_result = await forget_user_services(request, query_db, forget_user)
|
||||
if forget_user_result.is_success:
|
||||
logger.info(forget_user_result.message)
|
||||
return response_200(data=forget_user_result, message=forget_user_result.message)
|
||||
else:
|
||||
logger.warning(forget_user_result.message)
|
||||
return response_400(data="", message=forget_user_result.message)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return response_500(data="", message=str(e))
|
||||
# @loginController.post("/getSmsCode", response_model=SmsCode)
|
||||
# async def get_sms_code(request: Request, user: ResetUserModel, query_db: Session = Depends(get_db)):
|
||||
# try:
|
||||
# sms_result = await LoginService.get_sms_code_services(request, query_db, user)
|
||||
# if sms_result.is_success:
|
||||
# logger.info('获取成功')
|
||||
# return ResponseUtil.success(data=sms_result)
|
||||
# else:
|
||||
# logger.warning(sms_result.message)
|
||||
# return ResponseUtil.failure(msg=sms_result.message)
|
||||
# except Exception as e:
|
||||
# logger.exception(e)
|
||||
# return ResponseUtil.error(msg=str(e))
|
||||
#
|
||||
#
|
||||
# @loginController.post("/forgetPwd", response_model=CrudResponseModel)
|
||||
# async def forget_user_pwd(request: Request, forget_user: ResetUserModel, query_db: Session = Depends(get_db)):
|
||||
# try:
|
||||
# forget_user_result = await LoginService.forget_user_services(request, query_db, forget_user)
|
||||
# if forget_user_result.is_success:
|
||||
# logger.info(forget_user_result.message)
|
||||
# return ResponseUtil.success(data=forget_user_result, msg=forget_user_result.message)
|
||||
# else:
|
||||
# logger.warning(forget_user_result.message)
|
||||
# return ResponseUtil.failure(msg=forget_user_result.message)
|
||||
# except Exception as e:
|
||||
# logger.exception(e)
|
||||
# return ResponseUtil.error(msg=str(e))
|
||||
|
||||
|
||||
@loginController.post("/logout")
|
||||
async def logout(request: Request, token: Optional[str] = Depends(oauth2_scheme)):
|
||||
try:
|
||||
payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
|
||||
payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
|
||||
session_id: str = payload.get("session_id")
|
||||
await logout_services(request, session_id)
|
||||
await LoginService.logout_services(request, session_id)
|
||||
logger.info('退出成功')
|
||||
return ResponseUtil.success(msg="退出成功")
|
||||
except Exception as e:
|
||||
|
@@ -16,11 +16,8 @@ noticeController = APIRouter(prefix='/system/notice', dependencies=[Depends(Logi
|
||||
@noticeController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:notice:list'))])
|
||||
async def get_system_notice_list(request: Request, notice_page_query: NoticePageQueryModel = Depends(NoticePageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
notice_query = NoticeQueryModel(**notice_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
notice_query_result = NoticeService.get_notice_list_services(query_db, notice_query)
|
||||
# 分页操作
|
||||
notice_page_query_result = get_page_obj(notice_query_result, notice_page_query.page_num, notice_page_query.page_size)
|
||||
# 获取分页数据
|
||||
notice_page_query_result = NoticeService.get_notice_list_services(query_db, notice_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=notice_page_query_result)
|
||||
except Exception as e:
|
||||
|
@@ -18,11 +18,8 @@ postController = APIRouter(prefix='/system/post', dependencies=[Depends(LoginSer
|
||||
@postController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:post:list'))])
|
||||
async def get_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
post_query = PostModel(**post_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
post_query_result = PostService.get_post_list_services(query_db, post_query)
|
||||
# 分页操作
|
||||
post_page_query_result = get_page_obj(post_query_result, post_page_query.page_num, post_page_query.page_size)
|
||||
# 获取分页数据
|
||||
post_page_query_result = PostService.get_post_list_services(query_db, post_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=post_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -98,9 +95,8 @@ async def query_detail_system_post(request: Request, post_id: int, query_db: Ses
|
||||
@log_decorator(title='岗位管理', business_type=5)
|
||||
async def export_system_post_list(request: Request, post_page_query: PostPageQueryModel = Depends(PostPageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
post_query = PostModel(**post_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
post_query_result = PostService.get_post_list_services(query_db, post_query)
|
||||
post_query_result = PostService.get_post_list_services(query_db, post_page_query, is_page=False)
|
||||
post_export_result = PostService.export_post_list_services(post_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(post_export_result))
|
||||
|
@@ -7,7 +7,7 @@ from module_admin.service.dept_service import DeptService, DeptModel
|
||||
from module_admin.service.user_service import UserService, UserRoleQueryModel, UserRolePageQueryModel, CrudUserRoleModel
|
||||
from utils.response_util import *
|
||||
from utils.log_util import *
|
||||
from utils.page_util import *
|
||||
from utils.page_util import PageResponseModel
|
||||
from utils.common_util import bytes2file_response
|
||||
from module_admin.aspect.interface_auth import CheckUserInterfaceAuth
|
||||
from module_admin.aspect.data_scope import GetDataScope
|
||||
@@ -33,10 +33,7 @@ async def get_system_role_dept_tree(request: Request, role_id: int, query_db: Se
|
||||
@roleController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:role:list'))])
|
||||
async def get_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True))
|
||||
role_query_result = RoleService.get_role_list_services(query_db, role_query)
|
||||
# 分页操作
|
||||
role_page_query_result = get_page_obj(role_query_result, role_page_query.page_num, role_page_query.page_size)
|
||||
role_page_query_result = RoleService.get_role_list_services(query_db, role_page_query, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=role_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -134,9 +131,8 @@ async def query_detail_system_role(request: Request, role_id: int, query_db: Ses
|
||||
@log_decorator(title='角色管理', business_type=5)
|
||||
async def export_system_role_list(request: Request, role_page_query: RolePageQueryModel = Depends(RolePageQueryModel.as_form), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
role_query = RoleQueryModel(**role_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
role_query_result = RoleService.get_role_list_services(query_db, role_query)
|
||||
role_query_result = RoleService.get_role_list_services(query_db, role_page_query, is_page=False)
|
||||
role_export_result = RoleService.export_role_list_services(role_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(role_export_result))
|
||||
@@ -167,10 +163,7 @@ async def reset_system_role_status(request: Request, edit_role: AddRoleModel, qu
|
||||
@roleController.get("/authUser/allocatedList", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
|
||||
async def get_system_allocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True))
|
||||
role_user_allocated_query_result = RoleService.get_role_user_allocated_list_services(query_db, role_user_query)
|
||||
# 分页操作
|
||||
role_user_allocated_page_query_result = get_page_obj(role_user_allocated_query_result, user_role.page_num, user_role.page_size)
|
||||
role_user_allocated_page_query_result = RoleService.get_role_user_allocated_list_services(query_db, user_role, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=role_user_allocated_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -181,10 +174,7 @@ async def get_system_allocated_user_list(request: Request, user_role: UserRolePa
|
||||
@roleController.get("/authUser/unallocatedList", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('common'))])
|
||||
async def get_system_unallocated_user_list(request: Request, user_role: UserRolePageQueryModel = Depends(UserRolePageQueryModel.as_query), query_db: Session = Depends(get_db)):
|
||||
try:
|
||||
role_user_query = UserRoleQueryModel(**user_role.model_dump(by_alias=True))
|
||||
role_user_unallocated_query_result = RoleService.get_role_user_unallocated_list_services(query_db, role_user_query)
|
||||
# 分页操作
|
||||
role_user_unallocated_page_query_result = get_page_obj(role_user_unallocated_query_result, user_role.page_num, user_role.page_size)
|
||||
role_user_unallocated_page_query_result = RoleService.get_role_user_unallocated_list_services(query_db, user_role, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=role_user_unallocated_page_query_result)
|
||||
except Exception as e:
|
||||
|
@@ -5,7 +5,7 @@ from config.env import UploadConfig
|
||||
from module_admin.service.login_service import LoginService
|
||||
from module_admin.service.user_service import *
|
||||
from module_admin.service.dept_service import DeptService
|
||||
from utils.page_util import *
|
||||
from utils.page_util import PageResponseModel
|
||||
from utils.response_util import *
|
||||
from utils.log_util import *
|
||||
from utils.common_util import bytes2file_response
|
||||
@@ -32,11 +32,8 @@ async def get_system_dept_tree(request: Request, query_db: Session = Depends(get
|
||||
@userController.get("/list", response_model=PageResponseModel, dependencies=[Depends(CheckUserInterfaceAuth('system:user:list'))])
|
||||
async def get_system_user_list(request: Request, user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_query), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))):
|
||||
try:
|
||||
user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql)
|
||||
# 分页操作
|
||||
user_page_query_result = get_page_obj(user_query_result, user_page_query.page_num, user_page_query.page_size)
|
||||
# 获取分页数据
|
||||
user_page_query_result = UserService.get_user_list_services(query_db, user_page_query, data_scope_sql, is_page=True)
|
||||
logger.info('获取成功')
|
||||
return ResponseUtil.success(model_content=user_page_query_result)
|
||||
except Exception as e:
|
||||
@@ -272,9 +269,8 @@ async def export_system_user_template(request: Request, query_db: Session = Depe
|
||||
@log_decorator(title='用户管理', business_type=5)
|
||||
async def export_system_user_list(request: Request, user_page_query: UserPageQueryModel = Depends(UserPageQueryModel.as_form), query_db: Session = Depends(get_db), data_scope_sql: str = Depends(GetDataScope('SysUser'))):
|
||||
try:
|
||||
user_query = UserQueryModel(**user_page_query.model_dump(by_alias=True))
|
||||
# 获取全量数据
|
||||
user_query_result = UserService.get_user_list_services(query_db, user_query, data_scope_sql)
|
||||
user_query_result = UserService.get_user_list_services(query_db, user_page_query, data_scope_sql, is_page=False)
|
||||
user_export_result = UserService.export_user_list_services(user_query_result)
|
||||
logger.info('导出成功')
|
||||
return ResponseUtil.streaming(data=bytes2file_response(user_export_result))
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.config_do import SysConfig
|
||||
from module_admin.entity.vo.config_vo import *
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -39,14 +40,15 @@ class ConfigDao:
|
||||
return config_info
|
||||
|
||||
@classmethod
|
||||
def get_config_list(cls, db: Session, query_object: ConfigQueryModel):
|
||||
def get_config_list(cls, db: Session, query_object: ConfigPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取参数配置列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 参数配置列表信息对象
|
||||
"""
|
||||
config_list = db.query(SysConfig) \
|
||||
query = db.query(SysConfig) \
|
||||
.filter(SysConfig.config_name.like(f'%{query_object.config_name}%') if query_object.config_name else True,
|
||||
SysConfig.config_key.like(f'%{query_object.config_key}%') if query_object.config_key else True,
|
||||
SysConfig.config_type == query_object.config_type if query_object.config_type else True,
|
||||
@@ -55,7 +57,8 @@ class ConfigDao:
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
config_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return config_list
|
||||
|
||||
|
@@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.dict_do import SysDictType, SysDictData
|
||||
from module_admin.entity.vo.dict_vo import *
|
||||
from utils.time_format_util import list_format_datetime
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -52,14 +53,15 @@ class DictTypeDao:
|
||||
return list_format_datetime(dict_type_info)
|
||||
|
||||
@classmethod
|
||||
def get_dict_type_list(cls, db: Session, query_object: DictTypeQueryModel):
|
||||
def get_dict_type_list(cls, db: Session, query_object: DictTypePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取字典类型列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典类型列表信息对象
|
||||
"""
|
||||
dict_type_list = db.query(SysDictType) \
|
||||
query = db.query(SysDictType) \
|
||||
.filter(SysDictType.dict_name.like(f'%{query_object.dict_name}%') if query_object.dict_name else True,
|
||||
SysDictType.dict_type.like(f'%{query_object.dict_type}%') if query_object.dict_type else True,
|
||||
SysDictType.status == query_object.status if query_object.status else True,
|
||||
@@ -68,7 +70,8 @@ class DictTypeDao:
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
dict_type_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return dict_type_list
|
||||
|
||||
@@ -147,20 +150,22 @@ class DictDataDao:
|
||||
return dict_data_info
|
||||
|
||||
@classmethod
|
||||
def get_dict_data_list(cls, db: Session, query_object: DictDataModel):
|
||||
def get_dict_data_list(cls, db: Session, query_object: DictDataPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取字典数据列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
dict_data_list = db.query(SysDictData) \
|
||||
query = db.query(SysDictData) \
|
||||
.filter(SysDictData.dict_type == query_object.dict_type if query_object.dict_type else True,
|
||||
SysDictData.dict_label.like(f'%{query_object.dict_label}%') if query_object.dict_label else True,
|
||||
SysDictData.status == query_object.status if query_object.status else True
|
||||
) \
|
||||
.order_by(SysDictData.dict_sort) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
dict_data_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return dict_data_list
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.job_do import SysJob
|
||||
from module_admin.entity.vo.job_vo import *
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class JobDao:
|
||||
@@ -40,19 +41,21 @@ class JobDao:
|
||||
return job_info
|
||||
|
||||
@classmethod
|
||||
def get_job_list(cls, db: Session, query_object: JobModel):
|
||||
def get_job_list(cls, db: Session, query_object: JobPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取定时任务列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务列表信息对象
|
||||
"""
|
||||
job_list = db.query(SysJob) \
|
||||
query = db.query(SysJob) \
|
||||
.filter(SysJob.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
|
||||
SysJob.job_group == query_object.job_group if query_object.job_group else True,
|
||||
SysJob.status == query_object.status if query_object.status else True
|
||||
) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
job_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return job_list
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.job_do import SysJobLog
|
||||
from module_admin.entity.vo.job_vo import *
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -10,14 +11,15 @@ class JobLogDao:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_job_log_list(cls, db: Session, query_object: JobLogQueryModel):
|
||||
def get_job_log_list(cls, db: Session, query_object: JobLogPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取定时任务日志列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务日志列表信息对象
|
||||
"""
|
||||
job_log_list = db.query(SysJobLog) \
|
||||
query = db.query(SysJobLog) \
|
||||
.filter(SysJobLog.job_name.like(f'%{query_object.job_name}%') if query_object.job_name else True,
|
||||
SysJobLog.job_group == query_object.job_group if query_object.job_group else True,
|
||||
SysJobLog.status == query_object.status if query_object.status else True,
|
||||
@@ -26,7 +28,8 @@ class JobLogDao:
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
job_log_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return job_log_list
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.log_do import SysOperLog, SysLogininfor
|
||||
from module_admin.entity.vo.log_vo import *
|
||||
from utils.time_format_util import object_format_datetime, list_format_datetime
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -10,14 +10,15 @@ class OperationLogDao:
|
||||
操作日志管理模块数据库操作层
|
||||
"""
|
||||
@classmethod
|
||||
def get_operation_log_list(cls, db: Session, query_object: OperLogQueryModel):
|
||||
def get_operation_log_list(cls, db: Session, query_object: OperLogPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取操作日志列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 操作日志列表信息对象
|
||||
"""
|
||||
operation_log_list = db.query(SysOperLog) \
|
||||
query = db.query(SysOperLog) \
|
||||
.filter(SysOperLog.title.like(f'%{query_object.title}%') if query_object.title else True,
|
||||
SysOperLog.oper_name.like(f'%{query_object.oper_name}%') if query_object.oper_name else True,
|
||||
SysOperLog.business_type == query_object.business_type if query_object.business_type else True,
|
||||
@@ -27,7 +28,8 @@ class OperationLogDao:
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
)\
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
operation_log_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return operation_log_list
|
||||
|
||||
@@ -74,14 +76,15 @@ class LoginLogDao:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_login_log_list(cls, db: Session, query_object: LoginLogQueryModel):
|
||||
def get_login_log_list(cls, db: Session, query_object: LoginLogPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取登录日志列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 登录日志列表信息对象
|
||||
"""
|
||||
login_log_list = db.query(SysLogininfor) \
|
||||
query = db.query(SysLogininfor) \
|
||||
.filter(SysLogininfor.ipaddr.like(f'%{query_object.ipaddr}%') if query_object.ipaddr else True,
|
||||
SysLogininfor.user_name.like(f'%{query_object.user_name}%') if query_object.user_name else True,
|
||||
SysLogininfor.status == query_object.status if query_object.status else True,
|
||||
@@ -90,7 +93,8 @@ class LoginLogDao:
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
)\
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
login_log_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return login_log_list
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.notice_do import SysNotice
|
||||
from module_admin.entity.vo.notice_vo import *
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -40,14 +41,15 @@ class NoticeDao:
|
||||
return notice_info
|
||||
|
||||
@classmethod
|
||||
def get_notice_list(cls, db: Session, query_object: NoticeQueryModel):
|
||||
def get_notice_list(cls, db: Session, query_object: NoticePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取通知公告列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 通知公告列表信息对象
|
||||
"""
|
||||
notice_list = db.query(SysNotice) \
|
||||
query = db.query(SysNotice) \
|
||||
.filter(SysNotice.notice_title.like(f'%{query_object.notice_title}%') if query_object.notice_title else True,
|
||||
SysNotice.update_by.like(f'%{query_object.update_by}%') if query_object.update_by else True,
|
||||
SysNotice.notice_type == query_object.notice_type if query_object.notice_type else True,
|
||||
@@ -56,7 +58,8 @@ class NoticeDao:
|
||||
datetime.combine(datetime.strptime(query_object.end_time, '%Y-%m-%d'), time(23, 59, 59)))
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
notice_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return notice_list
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.post_do import SysPost
|
||||
from module_admin.entity.vo.post_vo import *
|
||||
from utils.page_util import PageUtil
|
||||
|
||||
|
||||
class PostDao:
|
||||
@@ -54,20 +55,22 @@ class PostDao:
|
||||
return post_info
|
||||
|
||||
@classmethod
|
||||
def get_post_list(cls, db: Session, query_object: PostModel):
|
||||
def get_post_list(cls, db: Session, query_object: PostPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取岗位列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 岗位列表信息对象
|
||||
"""
|
||||
post_list = db.query(SysPost) \
|
||||
query = db.query(SysPost) \
|
||||
.filter(SysPost.post_code.like(f'%{query_object.post_code}%') if query_object.post_code else True,
|
||||
SysPost.post_name.like(f'%{query_object.post_name}%') if query_object.post_name else True,
|
||||
SysPost.status == query_object.status if query_object.status else True
|
||||
) \
|
||||
.order_by(SysPost.post_sort) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
post_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return post_list
|
||||
|
||||
|
@@ -3,6 +3,7 @@ from sqlalchemy.orm import Session
|
||||
from module_admin.entity.do.role_do import SysRole, SysRoleMenu, SysRoleDept
|
||||
from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.vo.role_vo import *
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -85,14 +86,15 @@ class RoleDao:
|
||||
return role_info
|
||||
|
||||
@classmethod
|
||||
def get_role_list(cls, db: Session, query_object: RoleQueryModel):
|
||||
def get_role_list(cls, db: Session, query_object: RolePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取角色列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色列表信息对象
|
||||
"""
|
||||
role_list = db.query(SysRole) \
|
||||
query = db.query(SysRole) \
|
||||
.filter(SysRole.del_flag == 0,
|
||||
SysRole.role_name.like(f'%{query_object.role_name}%') if query_object.role_name else True,
|
||||
SysRole.role_key.like(f'%{query_object.role_key}%') if query_object.role_key else True,
|
||||
@@ -103,7 +105,8 @@ class RoleDao:
|
||||
if query_object.begin_time and query_object.end_time else True
|
||||
) \
|
||||
.order_by(SysRole.role_sort) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
role_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return role_list
|
||||
|
||||
|
@@ -6,7 +6,7 @@ from module_admin.entity.do.dept_do import SysDept
|
||||
from module_admin.entity.do.post_do import SysPost
|
||||
from module_admin.entity.do.menu_do import SysMenu
|
||||
from module_admin.entity.vo.user_vo import *
|
||||
from utils.time_format_util import list_format_datetime
|
||||
from utils.page_util import PageUtil
|
||||
from datetime import datetime, time
|
||||
|
||||
|
||||
@@ -137,15 +137,16 @@ class UserDao:
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def get_user_list(cls, db: Session, query_object: UserQueryModel, data_scope_sql: str):
|
||||
def get_user_list(cls, db: Session, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False):
|
||||
"""
|
||||
根据查询参数获取用户列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 用户列表信息对象
|
||||
"""
|
||||
user_list = db.query(SysUser, SysDept) \
|
||||
query = db.query(SysUser, SysDept) \
|
||||
.filter(SysUser.del_flag == 0,
|
||||
or_(SysUser.dept_id == query_object.dept_id, SysUser.dept_id.in_(
|
||||
db.query(SysDept.dept_id).filter(func.find_in_set(query_object.dept_id, SysDept.ancestors))
|
||||
@@ -163,7 +164,8 @@ class UserDao:
|
||||
eval(data_scope_sql)
|
||||
) \
|
||||
.outerjoin(SysDept, and_(SysUser.dept_id == SysDept.dept_id, SysDept.status == 0, SysDept.del_flag == 0)) \
|
||||
.distinct().all()
|
||||
.distinct()
|
||||
user_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return user_list
|
||||
|
||||
@@ -227,35 +229,15 @@ class UserDao:
|
||||
return allocated_role_list
|
||||
|
||||
@classmethod
|
||||
def get_user_role_unallocated_list_by_user_id(cls, db: Session, query_object: UserRoleQueryModel):
|
||||
"""
|
||||
根据用户id获取用户未分配的角色列表信息数据库操作
|
||||
:param db: orm对象
|
||||
:param query_object: 用户角色查询对象
|
||||
:return: 用户未分配的角色列表信息
|
||||
"""
|
||||
unallocated_role_list = db.query(SysRole) \
|
||||
.filter(
|
||||
SysRole.del_flag == 0,
|
||||
SysRole.role_id != 1,
|
||||
SysRole.role_name == query_object.role_name if query_object.role_name else True,
|
||||
SysRole.role_key == query_object.role_key if query_object.role_key else True,
|
||||
~SysRole.role_id.in_(
|
||||
db.query(SysUserRole.role_id).filter(SysUserRole.user_id == query_object.user_id)
|
||||
)
|
||||
).distinct().all()
|
||||
|
||||
return list_format_datetime(unallocated_role_list)
|
||||
|
||||
@classmethod
|
||||
def get_user_role_allocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel):
|
||||
def get_user_role_allocated_list_by_role_id(cls, db: Session, query_object: UserRolePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据角色id获取已分配的用户列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 用户角色查询对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色已分配的用户列表信息
|
||||
"""
|
||||
allocated_user_list = db.query(SysUser) \
|
||||
query = db.query(SysUser) \
|
||||
.filter(
|
||||
SysUser.del_flag == 0,
|
||||
SysUser.user_id != 1,
|
||||
@@ -264,19 +246,21 @@ class UserDao:
|
||||
SysUser.user_id.in_(
|
||||
db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id)
|
||||
)
|
||||
).distinct().all()
|
||||
).distinct()
|
||||
allocated_user_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return allocated_user_list
|
||||
|
||||
@classmethod
|
||||
def get_user_role_unallocated_list_by_role_id(cls, db: Session, query_object: UserRoleQueryModel):
|
||||
def get_user_role_unallocated_list_by_role_id(cls, db: Session, query_object: UserRolePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据角色id获取未分配的用户列表信息
|
||||
:param db: orm对象
|
||||
:param query_object: 用户角色查询对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色未分配的用户列表信息
|
||||
"""
|
||||
unallocated_user_list = db.query(SysUser) \
|
||||
query = db.query(SysUser) \
|
||||
.filter(
|
||||
SysUser.del_flag == 0,
|
||||
SysUser.user_id != 1,
|
||||
@@ -285,7 +269,8 @@ class UserDao:
|
||||
~SysUser.user_id.in_(
|
||||
db.query(SysUserRole.user_id).filter(SysUserRole.role_id == query_object.role_id)
|
||||
)
|
||||
).distinct().all()
|
||||
).distinct()
|
||||
unallocated_user_list = PageUtil.paginate(query, query_object.page_num, query_object.page_size, is_page)
|
||||
|
||||
return unallocated_user_list
|
||||
|
||||
|
@@ -37,8 +37,8 @@ class ConfigPageQueryModel(ConfigQueryModel):
|
||||
"""
|
||||
参数配置管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteConfigModel(BaseModel):
|
||||
|
@@ -58,8 +58,8 @@ class DictTypePageQueryModel(DictTypeQueryModel):
|
||||
"""
|
||||
字典类型管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteDictTypeModel(BaseModel):
|
||||
@@ -71,7 +71,7 @@ class DeleteDictTypeModel(BaseModel):
|
||||
dict_ids: str
|
||||
|
||||
|
||||
class DictDataQueryModel(DictTypeModel):
|
||||
class DictDataQueryModel(DictDataModel):
|
||||
"""
|
||||
字典数据管理不分页查询模型
|
||||
"""
|
||||
@@ -85,8 +85,8 @@ class DictDataPageQueryModel(DictDataQueryModel):
|
||||
"""
|
||||
字典数据管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteDictDataModel(BaseModel):
|
||||
|
@@ -63,8 +63,8 @@ class JobPageQueryModel(JobQueryModel):
|
||||
"""
|
||||
定时任务管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class EditJobModel(JobModel):
|
||||
@@ -97,8 +97,8 @@ class JobLogPageQueryModel(JobLogQueryModel):
|
||||
"""
|
||||
定时任务日志管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteJobLogModel(BaseModel):
|
||||
|
@@ -61,8 +61,8 @@ class OperLogPageQueryModel(OperLogQueryModel):
|
||||
"""
|
||||
操作日志管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteOperLogModel(BaseModel):
|
||||
@@ -89,8 +89,8 @@ class LoginLogPageQueryModel(LoginLogQueryModel):
|
||||
"""
|
||||
登录日志管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteLoginLogModel(BaseModel):
|
||||
|
@@ -14,6 +14,16 @@ class UserLogin(BaseModel):
|
||||
captcha_enabled: Optional[bool] = None
|
||||
|
||||
|
||||
class UserRegister(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
username: str
|
||||
password: str
|
||||
confirm_password: str
|
||||
code: Optional[str] = None
|
||||
uuid: Optional[str] = None
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
@@ -23,6 +33,7 @@ class CaptchaCode(BaseModel):
|
||||
model_config = ConfigDict(alias_generator=to_camel)
|
||||
|
||||
captcha_enabled: bool
|
||||
register_enabled: bool
|
||||
img: str
|
||||
uuid: str
|
||||
|
||||
|
@@ -37,8 +37,8 @@ class NoticePageQueryModel(NoticeQueryModel):
|
||||
"""
|
||||
通知公告管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeleteNoticeModel(BaseModel):
|
||||
|
@@ -37,8 +37,8 @@ class PostPageQueryModel(PostQueryModel):
|
||||
"""
|
||||
岗位管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class DeletePostModel(BaseModel):
|
||||
|
@@ -83,8 +83,8 @@ class RolePageQueryModel(RoleQueryModel):
|
||||
"""
|
||||
角色管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class RoleMenuQueryModel(BaseModel):
|
||||
|
@@ -124,8 +124,8 @@ class UserPageQueryModel(UserQueryModel):
|
||||
"""
|
||||
用户管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class AddUserModel(UserModel):
|
||||
@@ -176,8 +176,8 @@ class UserRolePageQueryModel(UserRoleQueryModel):
|
||||
"""
|
||||
用户角色关联管理分页查询模型
|
||||
"""
|
||||
page_num: int
|
||||
page_size: int
|
||||
page_num: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
|
||||
class SelectedRoleModel(RoleModel):
|
||||
|
@@ -11,16 +11,17 @@ class ConfigService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_config_list_services(cls, query_db: Session, query_object: ConfigQueryModel):
|
||||
def get_config_list_services(cls, query_db: Session, query_object: ConfigPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取参数配置列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 参数配置列表信息对象
|
||||
"""
|
||||
config_list_result = ConfigDao.get_config_list(query_db, query_object)
|
||||
config_list_result = ConfigDao.get_config_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(config_list_result)
|
||||
return config_list_result
|
||||
|
||||
@classmethod
|
||||
async def init_cache_sys_config_services(cls, query_db: Session, redis):
|
||||
@@ -35,10 +36,10 @@ class ConfigService:
|
||||
# 删除匹配的键
|
||||
if keys:
|
||||
await redis.delete(*keys)
|
||||
config_all = ConfigDao.get_config_list(query_db, ConfigQueryModel(**dict()))
|
||||
config_all = ConfigDao.get_config_list(query_db, ConfigPageQueryModel(**dict()), is_page=False)
|
||||
for config_obj in config_all:
|
||||
if config_obj.config_type == 'Y':
|
||||
await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.config_key}", config_obj.config_value)
|
||||
if config_obj.get('configType') == 'Y':
|
||||
await redis.set(f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:{config_obj.get('configKey')}", config_obj.get('configValue'))
|
||||
|
||||
@classmethod
|
||||
async def query_config_list_from_cache_services(cls, redis, config_key: str):
|
||||
|
@@ -12,16 +12,17 @@ class DictTypeService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_dict_type_list_services(cls, query_db: Session, query_object: DictTypeQueryModel):
|
||||
def get_dict_type_list_services(cls, query_db: Session, query_object: DictTypePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取字典类型列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典类型列表信息对象
|
||||
"""
|
||||
dict_type_list_result = DictTypeDao.get_dict_type_list(query_db, query_object)
|
||||
dict_type_list_result = DictTypeDao.get_dict_type_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(dict_type_list_result)
|
||||
return dict_type_list_result
|
||||
|
||||
@classmethod
|
||||
async def add_dict_type_services(cls, request: Request, query_db: Session, page_object: DictTypeModel):
|
||||
@@ -172,16 +173,17 @@ class DictDataService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_dict_data_list_services(cls, query_db: Session, query_object: DictDataModel):
|
||||
def get_dict_data_list_services(cls, query_db: Session, query_object: DictDataPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取字典数据列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 字典数据列表信息对象
|
||||
"""
|
||||
dict_data_list_result = DictDataDao.get_dict_data_list(query_db, query_object)
|
||||
dict_data_list_result = DictDataDao.get_dict_data_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(dict_data_list_result)
|
||||
return dict_data_list_result
|
||||
|
||||
@classmethod
|
||||
def query_dict_data_list_services(cls, query_db: Session, dict_type: str):
|
||||
|
@@ -1,7 +1,7 @@
|
||||
from module_admin.dao.job_log_dao import *
|
||||
from module_admin.dao.dict_dao import DictDataDao
|
||||
from module_admin.service.dict_service import Request, DictDataService
|
||||
from module_admin.entity.vo.common_vo import CrudResponseModel
|
||||
from utils.common_util import export_list2excel, CamelCaseUtil
|
||||
from utils.common_util import export_list2excel
|
||||
|
||||
|
||||
class JobLogService:
|
||||
@@ -10,16 +10,17 @@ class JobLogService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_job_log_list_services(cls, query_db: Session, query_object: JobLogQueryModel):
|
||||
def get_job_log_list_services(cls, query_db: Session, query_object: JobLogPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取定时任务日志列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务日志列表信息对象
|
||||
"""
|
||||
job_log_list_result = JobLogDao.get_job_log_list(query_db, query_object)
|
||||
job_log_list_result = JobLogDao.get_job_log_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(job_log_list_result)
|
||||
return job_log_list_result
|
||||
|
||||
@classmethod
|
||||
def add_job_log_services(cls, query_db: Session, page_object: JobLogModel):
|
||||
@@ -79,10 +80,10 @@ class JobLogService:
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@staticmethod
|
||||
def export_job_log_list_services(query_db, job_log_list: List):
|
||||
async def export_job_log_list_services(request: Request, job_log_list: List):
|
||||
"""
|
||||
导出定时任务日志信息service
|
||||
:param query_db: orm对象
|
||||
:param request: Request对象
|
||||
:param job_log_list: 定时任务日志信息列表
|
||||
:return: 定时任务日志信息对应excel的二进制数据
|
||||
"""
|
||||
@@ -103,17 +104,22 @@ class JobLogService:
|
||||
}
|
||||
|
||||
data = job_log_list
|
||||
job_group_list = DictDataDao.query_dict_data_list(query_db, dict_type='sys_job_group')
|
||||
job_group_option = [dict(label=item.dict_label, value=item.dict_value) for item in job_group_list]
|
||||
job_group_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_job_group')
|
||||
job_group_option = [dict(label=item.get('dictLabel'), value=item.get('dictValue')) for item in job_group_list]
|
||||
job_group_option_dict = {item.get('value'): item for item in job_group_option}
|
||||
job_executor_list = await DictDataService.query_dict_data_list_from_cache_services(request.app.state.redis, dict_type='sys_job_executor')
|
||||
job_executor_option = [dict(label=item.get('dictLabel'), value=item.get('dictValue')) for item in job_executor_list]
|
||||
job_executor_option_dict = {item.get('value'): item for item in job_executor_option}
|
||||
|
||||
for item in data:
|
||||
if item.get('status') == '0':
|
||||
item['status'] = '正常'
|
||||
else:
|
||||
item['status'] = '暂停'
|
||||
if str(item.get('job_group')) in job_group_option_dict.keys():
|
||||
item['job_group'] = job_group_option_dict.get(str(item.get('job_group'))).get('label')
|
||||
if str(item.get('jobGroup')) in job_group_option_dict.keys():
|
||||
item['jobGroup'] = job_group_option_dict.get(str(item.get('jobGroup'))).get('label')
|
||||
if str(item.get('jobExecutor')) in job_executor_option_dict.keys():
|
||||
item['jobExecutor'] = job_executor_option_dict.get(str(item.get('jobExecutor'))).get('label')
|
||||
new_data = [{mapping_dict.get(key): value for key, value in item.items() if mapping_dict.get(key)} for item in
|
||||
data]
|
||||
binary_data = export_list2excel(new_data)
|
||||
|
@@ -11,16 +11,17 @@ class JobService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_job_list_services(cls, query_db: Session, query_object: JobModel):
|
||||
def get_job_list_services(cls, query_db: Session, query_object: JobPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取定时任务列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 定时任务列表信息对象
|
||||
"""
|
||||
job_list_result = JobDao.get_job_list(query_db, query_object)
|
||||
job_list_result = JobDao.get_job_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(job_list_result)
|
||||
return job_list_result
|
||||
|
||||
@classmethod
|
||||
def add_job_services(cls, query_db: Session, page_object: JobModel):
|
||||
|
@@ -10,16 +10,17 @@ class OperationLogService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_operation_log_list_services(cls, query_db: Session, query_object: OperLogQueryModel):
|
||||
def get_operation_log_list_services(cls, query_db: Session, query_object: OperLogPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取操作日志列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 操作日志列表信息对象
|
||||
"""
|
||||
operation_log_list_result = OperationLogDao.get_operation_log_list(query_db, query_object)
|
||||
operation_log_list_result = OperationLogDao.get_operation_log_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(operation_log_list_result)
|
||||
return operation_log_list_result
|
||||
|
||||
@classmethod
|
||||
def add_operation_log_services(cls, query_db: Session, page_object: OperLogModel):
|
||||
@@ -131,16 +132,17 @@ class LoginLogService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_login_log_list_services(cls, query_db: Session, query_object: LoginLogQueryModel):
|
||||
def get_login_log_list_services(cls, query_db: Session, query_object: LoginLogPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取登录日志列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 登录日志列表信息对象
|
||||
"""
|
||||
operation_log_list_result = LoginLogDao.get_login_log_list(query_db, query_object)
|
||||
operation_log_list_result = LoginLogDao.get_login_log_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(operation_log_list_result)
|
||||
return operation_log_list_result
|
||||
|
||||
@classmethod
|
||||
def add_login_log_services(cls, query_db: Session, page_object: LogininforModel):
|
||||
|
@@ -9,12 +9,13 @@ from module_admin.service.user_service import *
|
||||
from module_admin.entity.vo.login_vo import *
|
||||
from module_admin.entity.vo.common_vo import CrudResponseModel
|
||||
from module_admin.dao.login_dao import *
|
||||
from config.env import JwtConfig, RedisInitKeyConfig
|
||||
from exceptions.exception import LoginException, AuthException
|
||||
from config.env import AppConfig, JwtConfig, RedisInitKeyConfig
|
||||
from config.get_db import get_db
|
||||
from utils.common_util import CamelCaseUtil
|
||||
from utils.pwd_util import *
|
||||
from utils.response_util import *
|
||||
from utils.message_util import *
|
||||
from config.get_db import get_db
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
|
||||
|
||||
@@ -60,8 +61,13 @@ class LoginService:
|
||||
if login_user.user_name == account_lock:
|
||||
logger.warning("账号已锁定,请稍后再试")
|
||||
raise LoginException(data="", message="账号已锁定,请稍后再试")
|
||||
# 判断是否开启验证码,开启则验证,否则不验证
|
||||
if login_user.captcha_enabled:
|
||||
# 判断请求是否来自于api文档,如果是返回指定格式的结果,用于修复api文档认证成功后token显示undefined的bug
|
||||
request_from_swagger = request.headers.get('referer').endswith('docs') if request.headers.get('referer') else False
|
||||
request_from_redoc = request.headers.get('referer').endswith('redoc') if request.headers.get('referer') else False
|
||||
# 判断是否开启验证码,开启则验证,否则不验证(dev模式下来自API文档的登录请求不检验)
|
||||
if not login_user.captcha_enabled or ((request_from_swagger or request_from_redoc) and AppConfig.app_env == 'dev'):
|
||||
pass
|
||||
else:
|
||||
await cls.__check_login_captcha(request, login_user)
|
||||
user = login_by_account(query_db, login_user.user_name)
|
||||
if not user:
|
||||
@@ -124,9 +130,9 @@ class LoginService:
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
expire = datetime.utcnow() + timedelta(minutes=30)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, JwtConfig.SECRET_KEY, algorithm=JwtConfig.ALGORITHM)
|
||||
encoded_jwt = jwt.encode(to_encode, JwtConfig.jwt_secret_key, algorithm=JwtConfig.jwt_algorithm)
|
||||
return encoded_jwt
|
||||
|
||||
@classmethod
|
||||
@@ -146,7 +152,7 @@ class LoginService:
|
||||
try:
|
||||
if token.startswith('Bearer'):
|
||||
token = token.split(' ')[1]
|
||||
payload = jwt.decode(token, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
|
||||
payload = jwt.decode(token, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
|
||||
user_id: str = payload.get("user_id")
|
||||
session_id: str = payload.get("session_id")
|
||||
if user_id is None:
|
||||
@@ -165,9 +171,9 @@ class LoginService:
|
||||
# redis_token = await request.app.state.redis.get(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}")
|
||||
if token == redis_token:
|
||||
await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{session_id}", redis_token,
|
||||
ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
|
||||
ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
|
||||
# await request.app.state.redis.set(f"{RedisInitKeyConfig.ACCESS_TOKEN.get('key')}:{user.user_basic_info.user_id}", redis_token,
|
||||
# ex=timedelta(minutes=JwtConfig.REDIS_TOKEN_EXPIRE_MINUTES))
|
||||
# ex=timedelta(minutes=JwtConfig.jwt_redis_expire_minutes))
|
||||
|
||||
role_id_list = [item.role_id for item in query_user.get('user_role_info')]
|
||||
if 1 in role_id_list:
|
||||
@@ -255,63 +261,104 @@ class LoginService:
|
||||
|
||||
return router_list
|
||||
|
||||
@classmethod
|
||||
async def register_user_services(cls, request: Request, query_db: Session, user_register: UserRegister):
|
||||
"""
|
||||
用户注册services
|
||||
:param request: Request对象
|
||||
:param query_db: orm对象
|
||||
:param user_register: 注册用户对象
|
||||
:return: 注册结果
|
||||
"""
|
||||
register_enabled = True if await request.app.state.redis.get(
|
||||
f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.registerUser") == 'true' else False
|
||||
captcha_enabled = True if await request.app.state.redis.get(
|
||||
f"{RedisInitKeyConfig.SYS_CONFIG.get('key')}:sys.account.captchaEnabled") == 'true' else False
|
||||
if user_register.password == user_register.confirm_password:
|
||||
if register_enabled:
|
||||
if captcha_enabled:
|
||||
captcha_value = await request.app.state.redis.get(
|
||||
f"{RedisInitKeyConfig.CAPTCHA_CODES.get('key')}:{user_register.uuid}")
|
||||
if not captcha_value:
|
||||
logger.warning("验证码已失效")
|
||||
return CrudResponseModel(is_success=False, message='验证码已失效')
|
||||
elif user_register.code != str(captcha_value):
|
||||
logger.warning("验证码错误")
|
||||
return CrudResponseModel(is_success=False, message='验证码错误')
|
||||
add_user = AddUserModel(
|
||||
userName=user_register.username,
|
||||
nickName=user_register.username,
|
||||
password=PwdUtil.get_password_hash(user_register.password)
|
||||
)
|
||||
result = UserService.add_user_services(query_db, add_user)
|
||||
return result
|
||||
else:
|
||||
result = dict(is_success=False, message='注册程序已关闭,禁止注册')
|
||||
else:
|
||||
result = dict(is_success=False, message='两次输入的密码不一致')
|
||||
|
||||
async def get_sms_code_services(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 CrudResponseModel(**result)
|
||||
|
||||
return SmsCode(**dict(is_success=True, sms_code=sms_code, session_id=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=False, sms_code='', session_id='', message='用户不存在'))
|
||||
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='用户不存在'))
|
||||
|
||||
async def forget_user_services(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='短信验证码不正确')
|
||||
@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)
|
||||
return CrudResponseModel(**result)
|
||||
|
||||
@classmethod
|
||||
async def logout_services(cls, request: Request, session_id: str):
|
||||
"""
|
||||
退出登录services
|
||||
:param request: Request对象
|
||||
: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'{current_user.user.user_id}_session_id')
|
||||
|
||||
async def logout_services(request: Request, session_id: str):
|
||||
"""
|
||||
退出登录services
|
||||
:param request: Request对象
|
||||
: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'{current_user.user.user_id}_session_id')
|
||||
|
||||
return True
|
||||
return True
|
||||
|
@@ -9,16 +9,17 @@ class NoticeService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_notice_list_services(cls, query_db: Session, query_object: NoticeQueryModel):
|
||||
def get_notice_list_services(cls, query_db: Session, query_object: NoticePageQueryModel, is_page: bool = True):
|
||||
"""
|
||||
获取通知公告列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 通知公告列表信息对象
|
||||
"""
|
||||
notice_list_result = NoticeDao.get_notice_list(query_db, query_object)
|
||||
notice_list_result = NoticeDao.get_notice_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(notice_list_result)
|
||||
return notice_list_result
|
||||
|
||||
@classmethod
|
||||
def add_notice_services(cls, query_db: Session, page_object: NoticeModel):
|
||||
|
@@ -25,7 +25,7 @@ class OnlineService:
|
||||
access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys]
|
||||
online_info_list = []
|
||||
for item in access_token_values_list:
|
||||
payload = jwt.decode(item, JwtConfig.SECRET_KEY, algorithms=[JwtConfig.ALGORITHM])
|
||||
payload = jwt.decode(item, JwtConfig.jwt_secret_key, algorithms=[JwtConfig.jwt_algorithm])
|
||||
online_dict = dict(
|
||||
token_id=payload.get('session_id'),
|
||||
user_name=payload.get('user_name'),
|
||||
|
@@ -8,16 +8,17 @@ class PostService:
|
||||
岗位管理模块服务层
|
||||
"""
|
||||
@classmethod
|
||||
def get_post_list_services(cls, query_db: Session, query_object: PostModel):
|
||||
def get_post_list_services(cls, query_db: Session, query_object: PostPageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取岗位列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 岗位列表信息对象
|
||||
"""
|
||||
post_list_result = PostDao.get_post_list(query_db, query_object)
|
||||
post_list_result = PostDao.get_post_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(post_list_result)
|
||||
return post_list_result
|
||||
|
||||
@classmethod
|
||||
def add_post_services(cls, query_db: Session, page_object: PostModel):
|
||||
|
@@ -1,7 +1,8 @@
|
||||
from module_admin.entity.vo.user_vo import UserInfoModel, UserRoleQueryModel
|
||||
from module_admin.entity.vo.user_vo import UserInfoModel, UserRolePageQueryModel
|
||||
from module_admin.entity.vo.common_vo import CrudResponseModel
|
||||
from module_admin.dao.user_dao import UserDao
|
||||
from module_admin.dao.role_dao import *
|
||||
from utils.page_util import PageResponseModel
|
||||
from utils.common_util import export_list2excel, CamelCaseUtil
|
||||
|
||||
|
||||
@@ -38,16 +39,17 @@ class RoleService:
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def get_role_list_services(cls, query_db: Session, query_object: RoleQueryModel):
|
||||
def get_role_list_services(cls, query_db: Session, query_object: RolePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
获取角色列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 角色列表信息对象
|
||||
"""
|
||||
role_list_result = RoleDao.get_role_list(query_db, query_object)
|
||||
role_list_result = RoleDao.get_role_list(query_db, query_object, is_page)
|
||||
|
||||
return CamelCaseUtil.transform_result(role_list_result)
|
||||
return role_list_result
|
||||
|
||||
@classmethod
|
||||
def add_role_services(cls, query_db: Session, page_object: AddRoleModel):
|
||||
@@ -230,27 +232,39 @@ class RoleService:
|
||||
return binary_data
|
||||
|
||||
@classmethod
|
||||
def get_role_user_allocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel):
|
||||
def get_role_user_allocated_list_services(cls, query_db: Session, page_object: UserRolePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据角色id获取已分配用户列表
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户关联角色对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 已分配用户列表
|
||||
"""
|
||||
query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object)
|
||||
allocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list]
|
||||
query_user_list = UserDao.get_user_role_allocated_list_by_role_id(query_db, page_object, is_page)
|
||||
allocated_list = PageResponseModel(
|
||||
**{
|
||||
**query_user_list.model_dump(by_alias=True),
|
||||
'rows': [UserInfoModel(**row) for row in query_user_list.rows]
|
||||
}
|
||||
)
|
||||
|
||||
return allocated_list
|
||||
|
||||
@classmethod
|
||||
def get_role_user_unallocated_list_services(cls, query_db: Session, page_object: UserRoleQueryModel):
|
||||
def get_role_user_unallocated_list_services(cls, query_db: Session, page_object: UserRolePageQueryModel, is_page: bool = False):
|
||||
"""
|
||||
根据角色id获取未分配用户列表
|
||||
:param query_db: orm对象
|
||||
:param page_object: 用户关联角色对象
|
||||
:param is_page: 是否开启分页
|
||||
:return: 未分配用户列表
|
||||
"""
|
||||
query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object)
|
||||
unallocated_list = [UserInfoModel(**CamelCaseUtil.transform_result(row)) for row in query_user_list]
|
||||
query_user_list = UserDao.get_user_role_unallocated_list_by_role_id(query_db, page_object, is_page)
|
||||
unallocated_list = PageResponseModel(
|
||||
**{
|
||||
**query_user_list.model_dump(by_alias=True),
|
||||
'rows': [UserInfoModel(**row) for row in query_user_list.rows]
|
||||
}
|
||||
)
|
||||
|
||||
return unallocated_list
|
||||
|
@@ -1,8 +1,9 @@
|
||||
from fastapi import UploadFile
|
||||
from module_admin.service.role_service import RoleService
|
||||
from module_admin.service.post_service import PostService
|
||||
from module_admin.service.post_service import PostService, PostPageQueryModel
|
||||
from module_admin.entity.vo.common_vo import CrudResponseModel
|
||||
from module_admin.dao.user_dao import *
|
||||
from utils.page_util import PageResponseModel
|
||||
from utils.pwd_util import *
|
||||
from utils.common_util import *
|
||||
|
||||
@@ -13,18 +14,27 @@ class UserService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_user_list_services(cls, query_db: Session, query_object: UserQueryModel, data_scope_sql: str):
|
||||
def get_user_list_services(cls, query_db: Session, query_object: UserPageQueryModel, data_scope_sql: str, is_page: bool = False):
|
||||
"""
|
||||
获取用户列表信息service
|
||||
:param query_db: orm对象
|
||||
:param query_object: 查询参数对象
|
||||
:param data_scope_sql: 数据权限对应的查询sql语句
|
||||
:param is_page: 是否开启分页
|
||||
:return: 用户列表信息对象
|
||||
"""
|
||||
query_result = UserDao.get_user_list(query_db, query_object, data_scope_sql)
|
||||
user_list_result = []
|
||||
if query_result:
|
||||
user_list_result = [{**CamelCaseUtil.transform_result(row[0]), 'dept': CamelCaseUtil.transform_result(row[1])} for row in query_result]
|
||||
query_result = UserDao.get_user_list(query_db, query_object, data_scope_sql, is_page)
|
||||
if is_page:
|
||||
user_list_result = PageResponseModel(
|
||||
**{
|
||||
**query_result.model_dump(by_alias=True),
|
||||
'rows': [{**row[0], 'dept': row[1]} for row in query_result.rows]
|
||||
}
|
||||
)
|
||||
else:
|
||||
user_list_result = []
|
||||
if query_result:
|
||||
user_list_result = [{**row[0], 'dept': row[1]} for row in query_result]
|
||||
|
||||
return user_list_result
|
||||
|
||||
@@ -134,7 +144,7 @@ class UserService:
|
||||
:param user_id: 用户id
|
||||
:return: 用户id对应的信息
|
||||
"""
|
||||
posts = PostService.get_post_list_services(query_db, PostModel(**{}))
|
||||
posts = PostService.get_post_list_services(query_db, PostPageQueryModel(**{}), is_page=False)
|
||||
roles = RoleService.get_role_select_option_services(query_db)
|
||||
if user_id != '':
|
||||
query_user = UserDao.get_user_detail_by_id(query_db, user_id=user_id)
|
||||
|
15
ruoyi-fastapi-backend/requirements.txt
Normal file
15
ruoyi-fastapi-backend/requirements.txt
Normal file
@@ -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
Normal file
83
ruoyi-fastapi-backend/server.py
Normal file
@@ -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
Normal file
10
ruoyi-fastapi-backend/sub_applications/handle.py
Normal file
@@ -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
Normal file
10
ruoyi-fastapi-backend/sub_applications/staticfiles.py
Normal file
@@ -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")
|
@@ -5,6 +5,7 @@ from openpyxl import Workbook
|
||||
from openpyxl.styles import Alignment, PatternFill
|
||||
from openpyxl.utils import get_column_letter
|
||||
from openpyxl.worksheet.datavalidation import DataValidation
|
||||
from sqlalchemy.engine.row import Row
|
||||
from typing import List
|
||||
from config.env import CachePathConfig
|
||||
|
||||
@@ -66,7 +67,10 @@ class CamelCaseUtil:
|
||||
return {cls.__to_camel_case(k): v for k, v in result.items()}
|
||||
# 如果是一组字典或其他类型的列表,遍历列表进行转换
|
||||
elif isinstance(result, list):
|
||||
return [cls.transform_result(row) if isinstance(row, dict) else cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) for row in result]
|
||||
return [cls.transform_result(row) if isinstance(row, (dict, Row)) else (cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) if row else row) for row in result]
|
||||
# 如果是sqlalchemy的Row实例,遍历Row进行转换
|
||||
elif isinstance(result, Row):
|
||||
return [cls.transform_result(row) if isinstance(row, dict) else (cls.transform_result({c.name: getattr(row, c.name) for c in row.__table__.columns}) if row else row) for row in result]
|
||||
# 如果是其他类型,如模型实例,先转换为字典
|
||||
else:
|
||||
return cls.transform_result({c.name: getattr(result, c.name) for c in result.__table__.columns})
|
||||
|
@@ -1,30 +1,9 @@
|
||||
import math
|
||||
from typing import Optional, List
|
||||
|
||||
from sqlalchemy.orm.query import Query
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from pydantic.alias_generators import to_camel
|
||||
|
||||
|
||||
class PageModel(BaseModel):
|
||||
"""
|
||||
分页模型
|
||||
"""
|
||||
offset: int
|
||||
page_num: int
|
||||
page_size: int
|
||||
total: int
|
||||
has_next: bool
|
||||
|
||||
|
||||
class PageObjectResponse(BaseModel):
|
||||
"""
|
||||
用户管理列表分页查询返回模型
|
||||
"""
|
||||
rows: List = []
|
||||
page_num: int
|
||||
page_size: int
|
||||
total: int
|
||||
has_next: bool
|
||||
from utils.common_util import CamelCaseUtil
|
||||
|
||||
|
||||
class PageResponseModel(BaseModel):
|
||||
@@ -40,33 +19,64 @@ class PageResponseModel(BaseModel):
|
||||
has_next: Optional[bool] = None
|
||||
|
||||
|
||||
def get_page_info(offset: int, page_num: int, page_size: int, count: int):
|
||||
class PageUtil:
|
||||
"""
|
||||
根据分页参数获取分页信息
|
||||
: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
|
||||
|
||||
@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:
|
||||
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
|
||||
no_paginated_data = query.all()
|
||||
result = CamelCaseUtil.transform_result(no_paginated_data)
|
||||
|
||||
result = dict(offset=res_offset, page_num=res_page_num, page_size=page_size, total=count, has_next=has_next)
|
||||
|
||||
return PageModel(**result)
|
||||
return result
|
||||
|
||||
|
||||
def get_page_obj(data_list: List, page_num: int, page_size: int):
|
||||
@@ -94,5 +104,3 @@ def get_page_obj(data_list: List, page_num: int, page_size: int):
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
@@ -187,115 +187,3 @@ class ResponseUtil:
|
||||
status_code=status.HTTP_200_OK,
|
||||
content=data
|
||||
)
|
||||
|
||||
|
||||
def response_200(*, data: Any = None, message="获取成功") -> Response:
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_200_OK,
|
||||
content=jsonable_encoder(
|
||||
{
|
||||
'code': 200,
|
||||
'message': message,
|
||||
'data': data,
|
||||
'success': 'true',
|
||||
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def response_400(*, data: Any = None, message: str = "获取失败") -> Response:
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
content=jsonable_encoder(
|
||||
{
|
||||
'code': 400,
|
||||
'message': message,
|
||||
'data': data,
|
||||
'success': 'false',
|
||||
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def response_401(*, data: Any = None, message: str = "获取失败") -> Response:
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
content=jsonable_encoder(
|
||||
{
|
||||
'code': 401,
|
||||
'message': message,
|
||||
'data': data,
|
||||
'success': 'false',
|
||||
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def response_403(*, data: Any = None, message: str = "获取失败") -> Response:
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
content=jsonable_encoder(
|
||||
{
|
||||
'code': 403,
|
||||
'message': message,
|
||||
'data': data,
|
||||
'success': 'false',
|
||||
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def response_500(*, data: Any = None, message: str = "接口异常") -> Response:
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
content=jsonable_encoder(
|
||||
{
|
||||
'code': 500,
|
||||
'message': message,
|
||||
'data': data,
|
||||
'success': 'false',
|
||||
'time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def streaming_response_200(*, data: Any = None):
|
||||
return StreamingResponse(
|
||||
status_code=status.HTTP_200_OK,
|
||||
content=data,
|
||||
)
|
||||
|
||||
|
||||
class AuthException(Exception):
|
||||
"""
|
||||
自定义令牌异常AuthException
|
||||
"""
|
||||
|
||||
def __init__(self, data: str = None, message: str = None):
|
||||
self.data = data
|
||||
self.message = message
|
||||
|
||||
|
||||
class PermissionException(Exception):
|
||||
"""
|
||||
自定义权限异常PermissionException
|
||||
"""
|
||||
|
||||
def __init__(self, data: str = None, message: str = None):
|
||||
self.data = data
|
||||
self.message = message
|
||||
|
||||
|
||||
class LoginException(Exception):
|
||||
"""
|
||||
自定义登录异常LoginException
|
||||
"""
|
||||
|
||||
def __init__(self, data: str = None, message: str = None):
|
||||
self.data = data
|
||||
self.message = message
|
||||
|
@@ -16,9 +16,12 @@
|
||||
"url": "https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@antv/g2plot": "^2.4.31",
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "10.6.1",
|
||||
"ant-design-vue": "^4.1.1",
|
||||
"axios": "0.27.2",
|
||||
"echarts": "5.4.3",
|
||||
"element-plus": "2.4.3",
|
||||
@@ -35,11 +38,12 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "4.5.0",
|
||||
"@vue/compiler-sfc": "3.3.9",
|
||||
"less": "^4.2.0",
|
||||
"sass": "1.69.5",
|
||||
"unplugin-auto-import": "0.17.1",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.0",
|
||||
"vite": "5.0.4",
|
||||
"vite-plugin-compression": "0.5.1",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.0"
|
||||
"vite-plugin-svg-icons": "2.0.1"
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const url = ref('https://gitee.com/y_project/RuoYi-Vue');
|
||||
const url = ref('https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI');
|
||||
|
||||
function goto() {
|
||||
window.open(url.value)
|
||||
|
@@ -64,7 +64,7 @@ export const constantRoutes = [
|
||||
children: [
|
||||
{
|
||||
path: '/index',
|
||||
component: () => import('@/views/index'),
|
||||
component: () => import('@/views/dashboard/index'),
|
||||
name: 'Index',
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
}
|
||||
|
@@ -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
Normal file
738
ruoyi-fastapi-frontend/src/views/dashboard/index.vue
Normal file
@@ -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
|
||||
>
|
||||
<span class="event">
|
||||
<span>{{ item.template1 }}</span
|
||||
>
|
||||
<a href="" style="color: #1890ff">
|
||||
{{ item?.group?.name }} </a
|
||||
> <span>{{ item.template2 }}</span
|
||||
>
|
||||
<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>
|
File diff suppressed because it is too large
Load Diff
@@ -76,8 +76,8 @@ const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const loginForm = ref({
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
username: "",
|
||||
password: "",
|
||||
rememberMe: false,
|
||||
code: "",
|
||||
uuid: ""
|
||||
@@ -140,6 +140,7 @@ function handleLogin() {
|
||||
function getCode() {
|
||||
getCodeImg().then(res => {
|
||||
captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled;
|
||||
register.value = res.registerEnabled === undefined ? false : res.registerEnabled;
|
||||
if (captchaEnabled.value) {
|
||||
codeUrl.value = "data:image/gif;base64," + res.img;
|
||||
loginForm.value.uuid = res.uuid;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="register">
|
||||
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
|
||||
<h3 class="title">若依后台管理系统</h3>
|
||||
<h3 class="title">vfadmin后台管理系统</h3>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="registerForm.username"
|
||||
@@ -70,7 +70,7 @@
|
||||
</el-form>
|
||||
<!-- 底部 -->
|
||||
<div class="el-register-footer">
|
||||
<span>Copyright © 2018-2023 ruoyi.vip All Rights Reserved.</span>
|
||||
<span>Copyright © 2024 insistence.tech All Rights Reserved.</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user