diff --git a/README.md b/README.md index b7cb2152e46973066d305bc5adb7c8fb92aef929..4bd2a86cc12b42039d2a3b85106450702d703212 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@

logo

-

Dash-FastAPI-Admin v2.1.0

+

Dash-FastAPI-Admin v2.1.1

基于Dash+FastAPI前后端分离的纯Python快速开发框架

- + diff --git a/dash-fastapi-backend/.env.dev b/dash-fastapi-backend/.env.dev index f79f19e370f30a93e5e1137469f609604d662909..8b2d308a7479ed16953aedf26c8b0130c39fad49 100644 --- a/dash-fastapi-backend/.env.dev +++ b/dash-fastapi-backend/.env.dev @@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0' # 应用端口 APP_PORT = 9099 # 应用版本 -APP_VERSION= '2.1.0' +APP_VERSION= '2.1.1' # 应用是否开启热重载 APP_RELOAD = true # 应用是否开启IP归属区域查询 diff --git a/dash-fastapi-backend/.env.prod b/dash-fastapi-backend/.env.prod index 55c011df164cc6b376d746166e84070daab9d991..fb893609b8077f939237cdb604dbc877243e9a45 100644 --- a/dash-fastapi-backend/.env.prod +++ b/dash-fastapi-backend/.env.prod @@ -10,7 +10,7 @@ APP_HOST = '0.0.0.0' # 应用端口 APP_PORT = 9099 # 应用版本 -APP_VERSION= '2.1.0' +APP_VERSION= '2.1.1' # 应用是否开启热重载 APP_RELOAD = false # 应用是否开启IP归属区域查询 diff --git a/dash-fastapi-backend/config/get_scheduler.py b/dash-fastapi-backend/config/get_scheduler.py index bf7547d0fdcb76de121ddca693ad67ca37f697c9..3fb66c41027ef468e6b08065e3ef98ddff76d5ca 100644 --- a/dash-fastapi-backend/config/get_scheduler.py +++ b/dash-fastapi-backend/config/get_scheduler.py @@ -1,11 +1,13 @@ import json from apscheduler.events import EVENT_ALL -from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor -from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.executors.asyncio import AsyncIOExecutor +from apscheduler.executors.pool import ProcessPoolExecutor from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.jobstores.redis import RedisJobStore from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore +from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger +from asyncio import iscoroutinefunction from datetime import datetime, timedelta from sqlalchemy.engine import create_engine from sqlalchemy.orm import sessionmaker @@ -109,9 +111,9 @@ job_stores = { ) ), } -executors = {'default': ThreadPoolExecutor(20), 'processpool': ProcessPoolExecutor(5)} +executors = {'default': AsyncIOExecutor(), 'processpool': ProcessPoolExecutor(5)} job_defaults = {'coalesce': False, 'max_instance': 1} -scheduler = BackgroundScheduler() +scheduler = AsyncIOScheduler() scheduler.configure(jobstores=job_stores, executors=executors, job_defaults=job_defaults) @@ -132,9 +134,7 @@ class SchedulerUtil: async with AsyncSessionLocal() as session: job_list = await JobDao.get_job_list_for_scheduler(session) for item in job_list: - query_job = cls.get_scheduler_job(job_id=str(item.job_id)) - if query_job: - cls.remove_scheduler_job(job_id=str(item.job_id)) + cls.remove_scheduler_job(job_id=str(item.job_id)) cls.add_scheduler_job(item) scheduler.add_listener(cls.scheduler_event_listener, EVENT_ALL) logger.info('系统初始定时任务加载成功') @@ -169,6 +169,10 @@ class SchedulerUtil: :param job_info: 任务对象信息 :return: """ + job_func = eval(job_info.invoke_target) + job_executor = job_info.job_executor + if iscoroutinefunction(job_func): + job_executor = 'default' scheduler.add_job( func=eval(job_info.invoke_target), trigger=MyCronTrigger.from_crontab(job_info.cron_expression), @@ -180,7 +184,7 @@ class SchedulerUtil: coalesce=True if job_info.misfire_policy == '2' else False, max_instances=3 if job_info.concurrent == '0' else 1, jobstore=job_info.job_group, - executor=job_info.job_executor, + executor=job_executor, ) @classmethod @@ -191,6 +195,10 @@ class SchedulerUtil: :param job_info: 任务对象信息 :return: """ + job_func = eval(job_info.invoke_target) + job_executor = job_info.job_executor + if iscoroutinefunction(job_func): + job_executor = 'default' scheduler.add_job( func=eval(job_info.invoke_target), trigger='date', @@ -203,7 +211,7 @@ class SchedulerUtil: coalesce=True if job_info.misfire_policy == '2' else False, max_instances=3 if job_info.concurrent == '0' else 1, jobstore=job_info.job_group, - executor=job_info.job_executor, + executor=job_executor, ) @classmethod @@ -214,7 +222,9 @@ class SchedulerUtil: :param job_id: 任务id :return: """ - scheduler.remove_job(job_id=str(job_id)) + query_job = cls.get_scheduler_job(job_id=job_id) + if query_job: + scheduler.remove_job(job_id=str(job_id)) @classmethod def scheduler_event_listener(cls, event): diff --git a/dash-fastapi-backend/module_admin/service/job_service.py b/dash-fastapi-backend/module_admin/service/job_service.py index 5a8c3bd163f7c669bb08f52e661a040f6cb8d9ec..91f784f901f59b8e94e6a9dd9dd4d2a267001f14 100644 --- a/dash-fastapi-backend/module_admin/service/job_service.py +++ b/dash-fastapi-backend/module_admin/service/job_service.py @@ -129,9 +129,7 @@ class JobService: raise ServiceException(message=f'修改定时任务{page_object.job_name}失败,定时任务已存在') try: await JobDao.edit_job_dao(query_db, edit_job) - query_job = SchedulerUtil.get_scheduler_job(job_id=edit_job.get('job_id')) - if query_job: - SchedulerUtil.remove_scheduler_job(job_id=edit_job.get('job_id')) + SchedulerUtil.remove_scheduler_job(job_id=edit_job.get('job_id')) if edit_job.get('status') == '0': job_info = await cls.job_detail_services(query_db, edit_job.get('job_id')) SchedulerUtil.add_scheduler_job(job_info=job_info) @@ -152,9 +150,7 @@ class JobService: :param page_object: 定时任务对象 :return: 执行一次定时任务结果 """ - query_job = SchedulerUtil.get_scheduler_job(job_id=page_object.job_id) - if query_job: - SchedulerUtil.remove_scheduler_job(job_id=page_object.job_id) + SchedulerUtil.remove_scheduler_job(job_id=page_object.job_id) job_info = await cls.job_detail_services(query_db, page_object.job_id) if job_info: SchedulerUtil.execute_scheduler_job_once(job_info=job_info) @@ -176,9 +172,7 @@ class JobService: try: for job_id in job_id_list: await JobDao.delete_job_dao(query_db, JobModel(job_id=job_id)) - query_job = SchedulerUtil.get_scheduler_job(job_id=job_id) - if query_job: - SchedulerUtil.remove_scheduler_job(job_id=job_id) + SchedulerUtil.remove_scheduler_job(job_id=job_id) await query_db.commit() return CrudResponseModel(is_success=True, message='删除成功') except Exception as e: diff --git a/dash-fastapi-backend/module_admin/service/menu_service.py b/dash-fastapi-backend/module_admin/service/menu_service.py index 2cf7639b38ebb71be15d8a60828654007c6b4feb..af636f7a04704946132fc102ae930947cfc1b524 100644 --- a/dash-fastapi-backend/module_admin/service/menu_service.py +++ b/dash-fastapi-backend/module_admin/service/menu_service.py @@ -99,9 +99,9 @@ class MenuService: :return: 新增菜单校验结果 """ if not await cls.check_menu_name_unique_services(query_db, page_object): - raise ServiceException(message=f'新增菜单{page_object.post_name}失败,菜单名称已存在') + raise ServiceException(message=f'新增菜单{page_object.menu_name}失败,菜单名称已存在') elif page_object.is_frame == MenuConstant.YES_FRAME and not StringUtil.is_http(page_object.path): - raise ServiceException(message=f'新增菜单{page_object.post_name}失败,地址必须以http(s)://开头') + raise ServiceException(message=f'新增菜单{page_object.menu_name}失败,地址必须以http(s)://开头') else: try: await MenuDao.add_menu_dao(query_db, page_object) @@ -124,11 +124,11 @@ class MenuService: menu_info = await cls.menu_detail_services(query_db, page_object.menu_id) if menu_info.menu_id: if not await cls.check_menu_name_unique_services(query_db, page_object): - raise ServiceException(message=f'修改菜单{page_object.post_name}失败,菜单名称已存在') + raise ServiceException(message=f'修改菜单{page_object.menu_name}失败,菜单名称已存在') elif page_object.is_frame == MenuConstant.YES_FRAME and not StringUtil.is_http(page_object.path): - raise ServiceException(message=f'修改菜单{page_object.post_name}失败,地址必须以http(s)://开头') + raise ServiceException(message=f'修改菜单{page_object.menu_name}失败,地址必须以http(s)://开头') elif page_object.menu_id == page_object.parent_id: - raise ServiceException(message=f'修改菜单{page_object.post_name}失败,上级菜单不能选择自己') + raise ServiceException(message=f'修改菜单{page_object.menu_name}失败,上级菜单不能选择自己') else: try: await MenuDao.edit_menu_dao(query_db, edit_menu) diff --git a/dash-fastapi-backend/module_task/scheduler_test.py b/dash-fastapi-backend/module_task/scheduler_test.py index 1c8441f9d6ccad2742d4b7c67d81a4220a5bdf29..b282732db1cee848b8c23ee782c2f3b5709ca131 100644 --- a/dash-fastapi-backend/module_task/scheduler_test.py +++ b/dash-fastapi-backend/module_task/scheduler_test.py @@ -2,6 +2,18 @@ from datetime import datetime def job(*args, **kwargs): + """ + 定时任务执行同步函数示例 + """ print(args) print(kwargs) - print(f'{datetime.now()}执行了') + print(f'{datetime.now()}同步函数执行了') + + +async def async_job(*args, **kwargs): + """ + 定时任务执行异步函数示例 + """ + print(args) + print(kwargs) + print(f'{datetime.now()}异步函数执行了') diff --git a/dash-fastapi-frontend/.env.dev b/dash-fastapi-frontend/.env.dev index 1c39a5161f51d4f62b247e381fe3ae453223e726..b2cb98fb59e470240e2fc2d2d0fd6f4689cee325 100644 --- a/dash-fastapi-frontend/.env.dev +++ b/dash-fastapi-frontend/.env.dev @@ -16,7 +16,7 @@ APP_HOST = '0.0.0.0' # 应用端口 APP_PORT = 8088 # 应用版本 -APP_VERSION= '2.1.0' +APP_VERSION= '2.1.1' # 应用是否开启debug模式 APP_DEBUG = true # flask-compress压缩配置 diff --git a/dash-fastapi-frontend/.env.prod b/dash-fastapi-frontend/.env.prod index 8d9b01e633b943a75bd7bd340cdef68e8edf0f5a..71e3dc37081fba9d2f480497f6d63f256ff718ae 100644 --- a/dash-fastapi-frontend/.env.prod +++ b/dash-fastapi-frontend/.env.prod @@ -16,7 +16,7 @@ APP_HOST = '0.0.0.0' # 应用端口 APP_PORT = 8088 # 应用版本 -APP_VERSION= '2.1.0' +APP_VERSION= '2.1.1' # 应用是否开启debug模式 APP_DEBUG = false # flask-compress压缩配置 diff --git a/requirements-pg.txt b/requirements-pg.txt index 24b2c643e5b48f7970db9423deb4c487a34a344e..26e3ff02b50e941f11dfbe2a4bef4351411ad0c6 100644 --- a/requirements-pg.txt +++ b/requirements-pg.txt @@ -1,11 +1,11 @@ APScheduler==3.10.4 asyncpg==0.30.0 -cachebox==4.1.2 -dash==2.18.1 +cachebox==4.2.3 +dash==2.18.2 DateTime==5.5 fastapi[all]==0.115.0 feffery-antd-charts==0.1.0rc5 -feffery-antd-components==0.3.8 +feffery-antd-components==0.3.10 feffery-markdown-components==0.2.10 feffery-utils-components==0.2.0rc24 Flask-Compress==1.15 @@ -16,11 +16,11 @@ pandas==2.2.2 passlib[bcrypt]==1.7.4 Pillow==10.4.0 psutil==6.0.0 -pydantic-validation-decorator==0.1.2 +pydantic-validation-decorator==0.1.4 PyJWT[crypto]==2.8.0 psycopg2==2.9.10 redis==5.0.7 requests==2.32.3 SQLAlchemy[asyncio]==2.0.31 user-agents==2.2.0 -waitress==3.0.0 +waitress==3.0.1 diff --git a/requirements.txt b/requirements.txt index 790b650f853e6db7df6501505c153ba4e610cef8..d88aec780ef5629e87b5aaca76fe5992fc8b6110 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ APScheduler==3.10.4 asyncmy==0.2.9 -cachebox==4.1.2 -dash==2.18.1 +cachebox==4.2.3 +dash==2.18.2 DateTime==5.5 fastapi[all]==0.115.0 feffery-antd-charts==0.1.0rc5 -feffery-antd-components==0.3.8 +feffery-antd-components==0.3.10 feffery-markdown-components==0.2.10 feffery-utils-components==0.2.0rc24 Flask-Compress==1.15 @@ -16,11 +16,11 @@ pandas==2.2.2 passlib[bcrypt]==1.7.4 Pillow==10.4.0 psutil==6.0.0 -pydantic-validation-decorator==0.1.2 +pydantic-validation-decorator==0.1.4 PyJWT[crypto]==2.8.0 PyMySQL==1.1.1 redis==5.0.7 requests==2.32.3 SQLAlchemy[asyncio]==2.0.31 user-agents==2.2.0 -waitress==3.0.0 +waitress==3.0.1