15 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
14 changed files with 83 additions and 57 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,12 +1,12 @@
<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.1</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.1-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.9-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">

View File

@@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0'
# 应用端口 # 应用端口
APP_PORT = 9099 APP_PORT = 9099
# 应用版本 # 应用版本
APP_VERSION= '1.3.1' 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.1' APP_VERSION= '1.3.3'
# 应用是否开启热重载 # 应用是否开启热重载
APP_RELOAD = false APP_RELOAD = false
# 应用是否开启IP归属区域查询 # 应用是否开启IP归属区域查询

View File

@@ -221,39 +221,40 @@ 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'
job_id = event.job_id if hasattr(event, 'job_id'):
query_job = cls.get_scheduler_job(job_id=job_id) job_id = event.job_id
if query_job: query_job = cls.get_scheduler_job(job_id=job_id)
query_job_info = query_job.__getstate__() if query_job:
# 获取任务名称 query_job_info = query_job.__getstate__()
job_name = query_job_info.get('name') # 获取任务名称
# 获取任务组名 job_name = query_job_info.get('name')
job_group = query_job._jobstore_alias # 获取任务组名
# 获取任务执行器 job_group = query_job._jobstore_alias
job_executor = query_job_info.get('executor') # 获取任务执行器
# 获取调用目标字符串 job_executor = query_job_info.get('executor')
invoke_target = query_job_info.get('func') # 获取调用目标字符串
# 获取调用函数位置参数 invoke_target = query_job_info.get('func')
job_args = ','.join(query_job_info.get('args')) # 获取调用函数位置参数
# 获取调用函数关键字参数 job_args = ','.join(query_job_info.get('args'))
job_kwargs = json.dumps(query_job_info.get('kwargs')) # 获取调用函数关键字参数
# 获取任务触发器 job_kwargs = json.dumps(query_job_info.get('kwargs'))
job_trigger = str(query_job_info.get('trigger')) # 获取任务触发器
# 构造日志消息 job_trigger = str(query_job_info.get('trigger'))
job_message = f"事件类型: {event_type}, 任务ID: {job_id}, 任务名称: {job_name}, 执行于{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" # 构造日志消息
job_log = JobLogModel( job_message = f"事件类型: {event_type}, 任务ID: {job_id}, 任务名称: {job_name}, 执行于{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
jobName=job_name, job_log = JobLogModel(
jobGroup=job_group, jobName=job_name,
jobExecutor=job_executor, jobGroup=job_group,
invokeTarget=invoke_target, jobExecutor=job_executor,
jobArgs=job_args, invokeTarget=invoke_target,
jobKwargs=job_kwargs, jobArgs=job_args,
jobTrigger=job_trigger, jobKwargs=job_kwargs,
jobMessage=job_message, jobTrigger=job_trigger,
status=status, jobMessage=job_message,
exceptionInfo=exception_info, status=status,
createTime=datetime.now(), exceptionInfo=exception_info,
) createTime=datetime.now(),
session = SessionLocal() )
JobLogService.add_job_log_services(session, job_log) session = SessionLocal()
session.close() JobLogService.add_job_log_services(session, job_log)
session.close()

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

@@ -16,6 +16,7 @@ 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 exceptions.exception import LoginException, ServiceException, ServiceWarning
from utils.log_util import logger
from utils.response_util import ResponseUtil from utils.response_util import ResponseUtil
@@ -116,13 +117,14 @@ class Log:
try: try:
# 调用原始函数 # 调用原始函数
result = await func(*args, **kwargs) result = await func(*args, **kwargs)
except LoginException as e: except (LoginException, ServiceWarning) as e:
logger.warning(e.message)
result = ResponseUtil.failure(data=e.data, msg=e.message) result = ResponseUtil.failure(data=e.data, msg=e.message)
except ServiceException as e: except ServiceException as e:
logger.error(e.message)
result = ResponseUtil.error(data=e.data, msg=e.message) result = ResponseUtil.error(data=e.data, msg=e.message)
except ServiceWarning as e:
result = ResponseUtil.failure(data=e.data, msg=e.message)
except Exception as e: except Exception as e:
logger.exception(e)
result = ResponseUtil.error(msg=str(e)) result = ResponseUtil.error(msg=str(e))
# 获取请求耗时 # 获取请求耗时
cost_time = float(time.time() - start_time) * 100 cost_time = float(time.time() - start_time) * 100
@@ -295,13 +297,14 @@ def log_decorator(
try: try:
# 调用原始函数 # 调用原始函数
result = await func(*args, **kwargs) result = await func(*args, **kwargs)
except LoginException as e: except (LoginException, ServiceWarning) as e:
logger.warning(e.message)
result = ResponseUtil.failure(data=e.data, msg=e.message) result = ResponseUtil.failure(data=e.data, msg=e.message)
except ServiceException as e: except ServiceException as e:
logger.error(e.message)
result = ResponseUtil.error(data=e.data, msg=e.message) result = ResponseUtil.error(data=e.data, msg=e.message)
except ServiceWarning as e:
result = ResponseUtil.failure(data=e.data, msg=e.message)
except Exception as e: except Exception as e:
logger.exception(e)
result = ResponseUtil.error(msg=str(e)) result = ResponseUtil.error(msg=str(e))
# 获取请求耗时 # 获取请求耗时
cost_time = float(time.time() - start_time) * 100 cost_time = float(time.time() - start_time) * 100

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.1", "version": "1.3.3",
"description": "vfadmin管理系统", "description": "vfadmin管理系统",
"author": "insistence", "author": "insistence",
"license": "MIT", "license": "MIT",