20 Commits

Author SHA1 Message Date
insistence
84a02a42aa !17 RuoYi-Vue3-FastAPI v1.3.3
Merge pull request !17 from insistence/develop
2024-09-04 01:16:02 +00:00
insistence
c750908c04 docs: 更新README文档 2024-09-04 09:07:42 +08:00
insistence
af89388294 chore: 升级版本至1.3.3 2024-09-04 09:07:34 +08:00
insistence
09380dc94f chore: 更新pydantic-validation-decorator版本为0.1.4,修复了一些底层bug 2024-09-04 09:02:13 +08:00
insistence
82c84b2afe perf: 日志装饰器异常处理增加logger打印日志 2024-09-04 09:01:41 +08:00
insistence
f404aba689 fix: 修复在线用户模块条件查询无效的问题 2024-08-17 16:52:46 +08:00
insistence
9013b1917e perf: 优化在线用户模块前后端字段描述一致 2024-08-17 16:51:38 +08:00
insistence
c415dfa8e1 !16 RuoYi-Vue3-FastAPI v1.3.2
Merge pull request !16 from insistence/develop
2024-08-08 01:38:22 +00:00
insistence
5765e967ae docs: 更新README文档 2024-08-08 09:28:33 +08:00
insistence
1ba4d959ce chore: 升级版本至1.3.2 2024-08-08 09:27:51 +08:00
insistence
df8ab6bc55 chore: 调整.gitignore策略 2024-08-08 09:24:09 +08:00
insistence
ca6668331f style: 优化添加中间件函数注释 2024-08-08 09:20:57 +08:00
insistence
d49d05b776 feat: 新增gzip压缩中间件 2024-08-08 09:18:07 +08:00
insistence
d8e3f7dca1 fix: 修复定时任务监听函数中事件没有job_id报错的问题 2024-08-07 14:41:57 +08:00
insistence
5ee1a64587 fix: 修复分页函数计算has_next错误的问题 #10 2024-08-02 10:27:06 +08:00
insistence
9a31c21943 !15 RuoYi-Vue3-FastAPI v1.3.1
Merge pull request !15 from insistence/develop
2024-07-31 03:24:06 +00:00
insistence
e52f0f42cb docs: 更新README文档 2024-07-31 11:14:38 +08:00
insistence
1f51525dfa chore: 升级版本至1.3.1 2024-07-31 11:13:57 +08:00
insistence
a1c77829cd perf: 补充定时任务违规字符串 2024-07-31 10:59:01 +08:00
insistence
07771c180d fix: 修复1.3.0版本采用新的异常处理机制后日志装饰器无法记录异常日志的问题 2024-07-27 16:31:01 +08:00
15 changed files with 138 additions and 62 deletions

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ lib64/
parts/ parts/
sdist/ sdist/
var/ var/
vf_admin/
wheels/ wheels/
share/python-wheels/ share/python-wheels/
*.egg-info/ *.egg-info/

View File

@@ -1,22 +1,17 @@
<p align="center"> <p align="center">
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png"> <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</p> </p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.3.0</h1> <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Vue3-FastAPI v1.3.3</h1>
<h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4> <h4 align="center">基于RuoYi-Vue3+FastAPI前后端分离的快速开发框架</h4>
<p align="center"> <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://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://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.3.0-brightgreen.svg"></a> <a href="https://gitee.com/insistence2022/RuoYi-Vue3-FastAPI"><img src="https://img.shields.io/badge/RuoYiVue3FastAPI-v1.3.3-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> <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/python-≥3.9-blue">
<img src="https://img.shields.io/badge/MySQL-≥5.7-blue"> <img src="https://img.shields.io/badge/MySQL-≥5.7-blue">
</p> </p>
## 平台简介 ## 平台简介
RuoYi-Vue3-FastAPI是一套全部开源的快速开发平台毫无保留给个人及企业免费使用。 RuoYi-Vue3-FastAPI是一套全部开源的快速开发平台毫无保留给个人及企业免费使用。

View File

@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口 # 应用端口
APP_PORT = 9099 APP_PORT = 9099
# 应用版本 # 应用版本
APP_VERSION= '1.3.0' APP_VERSION= '1.3.3'
# 应用是否开启热重载 # 应用是否开启热重载
APP_RELOAD = true APP_RELOAD = true
# 应用是否开启IP归属区域查询 # 应用是否开启IP归属区域查询

View File

@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口 # 应用端口
APP_PORT = 9099 APP_PORT = 9099
# 应用版本 # 应用版本
APP_VERSION= '1.3.0' APP_VERSION= '1.3.3'
# 应用是否开启热重载 # 应用是否开启热重载
APP_RELOAD = false APP_RELOAD = false
# 应用是否开启IP归属区域查询 # 应用是否开启IP归属区域查询

View File

@@ -76,7 +76,7 @@ class JobConstant:
""" """
定时任务常量 定时任务常量
JOB_ERROR_LIST: 定时任务禁止调用模块列表 JOB_ERROR_LIST: 定时任务禁止调用模块及违规字符串列表
JOB_WHITE_LIST: 定时任务允许调用模块列表 JOB_WHITE_LIST: 定时任务允许调用模块列表
""" """
@@ -84,11 +84,46 @@ class JobConstant:
'app', 'app',
'config', 'config',
'exceptions', 'exceptions',
'import ',
'middlewares', 'middlewares',
'module_admin', 'module_admin',
'open(',
'os.',
'server', 'server',
'sub_applications', 'sub_applications',
'subprocess.',
'sys.',
'utils', 'utils',
'while ',
'__import__',
'"',
"'",
',',
'?',
':',
';',
'/',
'|',
'+',
'-',
'=',
'~',
'!',
'#',
'$',
'%',
'^',
'&',
'*',
'<',
'>',
'(',
')',
'[',
']',
'{',
'}',
' ',
] ]
JOB_WHITE_LIST = ['module_task'] JOB_WHITE_LIST = ['module_task']

View File

@@ -221,6 +221,7 @@ class SchedulerUtil:
if event_type == 'JobExecutionEvent' and event.exception: if event_type == 'JobExecutionEvent' and event.exception:
exception_info = str(event.exception) exception_info = str(event.exception)
status = '1' status = '1'
if hasattr(event, 'job_id'):
job_id = event.job_id job_id = event.job_id
query_job = cls.get_scheduler_job(job_id=job_id) query_job = cls.get_scheduler_job(job_id=job_id)
if query_job: if query_job:

View File

@@ -3,6 +3,12 @@ from fastapi.middleware.cors import CORSMiddleware
def add_cors_middleware(app: FastAPI): def add_cors_middleware(app: FastAPI):
"""
添加跨域中间件
:param app: FastAPI对象
:return:
"""
# 前端页面url # 前端页面url
origins = [ origins = [
'http://localhost:80', 'http://localhost:80',

View File

@@ -0,0 +1,12 @@
from fastapi import FastAPI
from starlette.middleware.gzip import GZipMiddleware
def add_gzip_middleware(app: FastAPI):
"""
添加gzip压缩中间件
:param app: FastAPI对象
:return:
"""
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=9)

View File

@@ -1,5 +1,6 @@
from fastapi import FastAPI from fastapi import FastAPI
from middlewares.cors_middleware import add_cors_middleware from middlewares.cors_middleware import add_cors_middleware
from middlewares.gzip_middleware import add_gzip_middleware
def handle_middleware(app: FastAPI): def handle_middleware(app: FastAPI):
@@ -8,3 +9,5 @@ def handle_middleware(app: FastAPI):
""" """
# 加载跨域中间件 # 加载跨域中间件
add_cors_middleware(app) add_cors_middleware(app)
# 加载gzip压缩中间件
add_gzip_middleware(app)

View File

@@ -15,6 +15,9 @@ from module_admin.service.log_service import LoginLogService, OperationLogServic
from module_admin.service.login_service import LoginService from module_admin.service.login_service import LoginService
from config.enums import BusinessType from config.enums import BusinessType
from config.env import AppConfig from config.env import AppConfig
from exceptions.exception import LoginException, ServiceException, ServiceWarning
from utils.log_util import logger
from utils.response_util import ResponseUtil
class Log: class Log:
@@ -111,8 +114,18 @@ class Log:
loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S'), loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S'),
) )
kwargs['form_data'].login_info = login_log kwargs['form_data'].login_info = login_log
try:
# 调用原始函数 # 调用原始函数
result = await func(*args, **kwargs) result = await func(*args, **kwargs)
except (LoginException, ServiceWarning) as e:
logger.warning(e.message)
result = ResponseUtil.failure(data=e.data, msg=e.message)
except ServiceException as e:
logger.error(e.message)
result = ResponseUtil.error(data=e.data, msg=e.message)
except Exception as e:
logger.exception(e)
result = ResponseUtil.error(msg=str(e))
# 获取请求耗时 # 获取请求耗时
cost_time = float(time.time() - start_time) * 100 cost_time = float(time.time() - start_time) * 100
# 判断请求是否来自api文档 # 判断请求是否来自api文档
@@ -281,8 +294,18 @@ def log_decorator(
loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S'), loginTime=oper_time.strftime('%Y-%m-%d %H:%M:%S'),
) )
kwargs['form_data'].login_info = login_log kwargs['form_data'].login_info = login_log
try:
# 调用原始函数 # 调用原始函数
result = await func(*args, **kwargs) result = await func(*args, **kwargs)
except (LoginException, ServiceWarning) as e:
logger.warning(e.message)
result = ResponseUtil.failure(data=e.data, msg=e.message)
except ServiceException as e:
logger.error(e.message)
result = ResponseUtil.error(data=e.data, msg=e.message)
except Exception as e:
logger.exception(e)
result = ResponseUtil.error(msg=str(e))
# 获取请求耗时 # 获取请求耗时
cost_time = float(time.time() - start_time) * 100 cost_time = float(time.time() - start_time) * 100
# 判断请求是否来自api文档 # 判断请求是否来自api文档

View File

@@ -13,10 +13,10 @@ class OnlineModel(BaseModel):
model_config = ConfigDict(alias_generator=to_camel) model_config = ConfigDict(alias_generator=to_camel)
token_id: Optional[str] = Field(default=None, description='会话编号') token_id: Optional[str] = Field(default=None, description='会话编号')
user_name: Optional[str] = Field(default=None, description='部门名称') user_name: Optional[str] = Field(default=None, description='登录名称')
dept_name: Optional[str] = Field(default=None, description='用户名称') dept_name: Optional[str] = Field(default=None, description='所属部门')
ipaddr: Optional[str] = Field(default=None, description='登录IP地址') ipaddr: Optional[str] = Field(default=None, description='主机')
login_location: Optional[str] = Field(default=None, description='登录地') login_location: Optional[str] = Field(default=None, description='登录地')
browser: Optional[str] = Field(default=None, description='浏览器类型') browser: Optional[str] = Field(default=None, description='浏览器类型')
os: Optional[str] = Field(default=None, description='操作系统') os: Optional[str] = Field(default=None, description='操作系统')
login_time: Optional[datetime] = Field(default=None, description='登录时间') login_time: Optional[datetime] = Field(default=None, description='登录时间')

View File

@@ -22,7 +22,7 @@ class OnlineService:
:param query_object: 查询参数对象 :param query_object: 查询参数对象
:return: 在线用户列表信息 :return: 在线用户列表信息
""" """
access_token_keys = await request.app.state.redis.keys(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}*") access_token_keys = await request.app.state.redis.keys(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}*')
if not access_token_keys: if not access_token_keys:
access_token_keys = [] access_token_keys = []
access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys] access_token_values_list = [await request.app.state.redis.get(key) for key in access_token_keys]
@@ -40,11 +40,11 @@ class OnlineService:
login_time=payload.get('login_info').get('loginTime'), login_time=payload.get('login_info').get('loginTime'),
) )
if query_object.user_name and not query_object.ipaddr: if query_object.user_name and not query_object.ipaddr:
if query_object.user_name == payload.get('login_info').get('ipaddr'): if query_object.user_name == payload.get('user_name'):
online_info_list = [online_dict] online_info_list = [online_dict]
break break
elif not query_object.user_name and query_object.ipaddr: elif not query_object.user_name and query_object.ipaddr:
if query_object.ipaddr == payload.get('ipaddr'): if query_object.ipaddr == payload.get('login_info').get('ipaddr'):
online_info_list = [online_dict] online_info_list = [online_dict]
break break
elif query_object.user_name and query_object.ipaddr: elif query_object.user_name and query_object.ipaddr:
@@ -70,7 +70,7 @@ class OnlineService:
if page_object.token_ids: if page_object.token_ids:
token_id_list = page_object.token_ids.split(',') token_id_list = page_object.token_ids.split(',')
for token_id in token_id_list: for token_id in token_id_list:
await request.app.state.redis.delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{token_id}") await request.app.state.redis.delete(f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{token_id}')
return CrudResponseModel(is_success=True, message='强退成功') return CrudResponseModel(is_success=True, message='强退成功')
else: else:
raise ServiceException(message='传入session_id为空') raise ServiceException(message='传入session_id为空')

View File

@@ -8,7 +8,7 @@ pandas==2.2.2
passlib[bcrypt]==1.7.4 passlib[bcrypt]==1.7.4
Pillow==10.4.0 Pillow==10.4.0
psutil==6.0.0 psutil==6.0.0
pydantic-validation-decorator==0.1.2 pydantic-validation-decorator==0.1.4
PyJWT[crypto]==2.8.0 PyJWT[crypto]==2.8.0
PyMySQL==1.1.1 PyMySQL==1.1.1
redis==5.0.7 redis==5.0.7

View File

@@ -71,7 +71,7 @@ class PageUtil:
paginated_data.append(row[0]) paginated_data.append(row[0])
else: else:
paginated_data.append(row) paginated_data.append(row)
has_next = True if math.ceil(len(paginated_data) / page_size) > page_num else False has_next = math.ceil(total / page_size) > page_num
result = PageResponseModel( result = PageResponseModel(
rows=CamelCaseUtil.transform_result(paginated_data), rows=CamelCaseUtil.transform_result(paginated_data),
pageNum=page_num, pageNum=page_num,

View File

@@ -1,6 +1,6 @@
{ {
"name": "vfadmin", "name": "vfadmin",
"version": "1.3.0", "version": "1.3.3",
"description": "vfadmin管理系统", "description": "vfadmin管理系统",
"author": "insistence", "author": "insistence",
"license": "MIT", "license": "MIT",