定时任务完成
parent
29993485bb
commit
3226d7eb7e
|
@ -101,11 +101,14 @@ npm run build:prod
|
|||
5. 执行迁移命令:
|
||||
python3 manage.py makemigrations
|
||||
python3 manage.py migrate
|
||||
5. 初始化数据
|
||||
6. 初始化数据
|
||||
python3 manage.py init
|
||||
6. 启动项目
|
||||
7. 启动项目
|
||||
python3 manage.py runserver 0.0.0.0:8000
|
||||
|
||||
定时任务启动命令:
|
||||
celery -A application worker -B --loglevel=info
|
||||
|
||||
初始账号:admin 密码:123456
|
||||
|
||||
后端接口文档地址:http://127.0.0.1:8000/docs/
|
||||
|
|
|
@ -113,28 +113,27 @@ services:
|
|||
- dvadmin_net
|
||||
|
||||
|
||||
# docker-celery:
|
||||
# build:
|
||||
# context: .
|
||||
# dockerfile: ./docker_env/celery/Dockerfile
|
||||
# # image: celery:1.0
|
||||
# container_name: docker-celery
|
||||
# working_dir: /dvadmin-backend
|
||||
# # command: bash
|
||||
# depends_on:
|
||||
# - dvadmin-mysql
|
||||
# - dvadmin-redis
|
||||
# - dvadmin-django
|
||||
# volumes:
|
||||
# - ./dvadmin-backend:/dvadmin-backend
|
||||
# - ./logs/log:/var/log
|
||||
# ports:
|
||||
# - "5672:5672"
|
||||
# expose:
|
||||
# - "5672"
|
||||
# restart: always
|
||||
# networks:
|
||||
# - dvadmin_net
|
||||
dvadmin-celery:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./docker_env/celery/Dockerfile
|
||||
# image: django:2.2
|
||||
container_name: dvadmin-celery
|
||||
working_dir: /dvadmin-backend
|
||||
# command: bash -c "python manage.py makemigrations && python manage.py migrate && python manage.py collectstatic --noinput && python manage.py runserver"
|
||||
# command: bash
|
||||
depends_on:
|
||||
- dvadmin-mysql
|
||||
- dvadmin-redis
|
||||
environment:
|
||||
- REDIS_HOST=dvadmin-redis
|
||||
- DATABASE_HOST=dvadmin-mysql
|
||||
volumes:
|
||||
- ./dvadmin-backend:/dvadmin-backend
|
||||
- ./logs/log:/var/log
|
||||
restart: always
|
||||
networks:
|
||||
- dvadmin_net
|
||||
|
||||
|
||||
dvadmin-nginx:
|
||||
|
|
|
@ -12,5 +12,5 @@ COPY ./docker_env/celery/requirement.txt /
|
|||
RUN mkdir /backend
|
||||
WORKDIR /backend
|
||||
RUN python3.7 -m pip install -i https://mirrors.aliyun.com/pypi/simple/ -r /requirement.txt --use-feature=2020-resolver
|
||||
CMD ["celery", "-A", "azcrm", "worker", "-B", "--loglevel=info"]
|
||||
CMD ["celery", "-A", "application", "worker", "-B", "--loglevel=info"]
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import os
|
||||
|
||||
import django
|
||||
from celery import Celery, platforms
|
||||
from django.conf import settings
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', "application.settings")
|
||||
django.setup()
|
||||
|
||||
app = Celery(f"dvadmin")
|
||||
|
||||
app.config_from_object('django.conf:settings')
|
||||
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
|
||||
platforms.C_FORCE_ROOT = True
|
|
@ -45,7 +45,7 @@ INSTALLED_APPS = [
|
|||
'rest_framework',
|
||||
'corsheaders',
|
||||
'captcha',
|
||||
'djcelery',
|
||||
'django_celery_beat',
|
||||
# 自定义app
|
||||
'apps.vadmin.permission',
|
||||
'apps.vadmin.op_drf',
|
||||
|
@ -320,3 +320,5 @@ CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'
|
|||
API_LOG_ENABLE = True
|
||||
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
|
||||
# API_LOG_METHODS = ['POST', 'DELETE'] # ['POST', 'DELETE']
|
||||
BROKER_URL = f'redis://:{REDIS_PASSWORD if REDIS_PASSWORD else ""}@{os.getenv("REDIS_HOST") or REDIS_HOST}:{REDIS_PORT}/2' #Broker使用Redis, 使用0数据库(暂时不是很清楚原理)
|
||||
CELERYBEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' #Backend数据库
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import django_filters
|
||||
from djcelery.models import IntervalSchedule, CrontabSchedule, PeriodicTask
|
||||
from django_celery_beat.models import IntervalSchedule, CrontabSchedule, PeriodicTask
|
||||
|
||||
|
||||
class IntervalScheduleFilter(django_filters.rest_framework.FilterSet):
|
||||
|
@ -11,7 +11,7 @@ class IntervalScheduleFilter(django_filters.rest_framework.FilterSet):
|
|||
class CrontabScheduleFilter(django_filters.rest_framework.FilterSet):
|
||||
class Meta:
|
||||
model = CrontabSchedule
|
||||
fields = '__all__'
|
||||
exclude = ('timezone',)
|
||||
|
||||
|
||||
class PeriodicTaskFilter(django_filters.rest_framework.FilterSet):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from djcelery.models import IntervalSchedule, CrontabSchedule, PeriodicTask
|
||||
from django_celery_beat.models import IntervalSchedule, CrontabSchedule, PeriodicTask
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..op_drf.serializers import CustomModelSerializer
|
||||
|
@ -23,7 +23,7 @@ class CrontabScheduleSerializer(CustomModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = CrontabSchedule
|
||||
fields = '__all__'
|
||||
exclude = ('timezone',)
|
||||
|
||||
|
||||
class PeriodicTaskSerializer(CustomModelSerializer):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from djcelery.admin import TaskSelectWidget
|
||||
from djcelery.models import IntervalSchedule, CrontabSchedule, PeriodicTask
|
||||
from django_celery_beat.admin import TaskSelectWidget
|
||||
from django_celery_beat.models import IntervalSchedule, CrontabSchedule, PeriodicTask
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from ..celery.filters import IntervalScheduleFilter, CrontabScheduleFilter, PeriodicTaskFilter
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import datetime
|
||||
import logging
|
||||
|
||||
from captcha.models import CaptchaStore
|
||||
|
||||
from ..utils.decorators import BaseCeleryApp
|
||||
from ..utils.response import SuccessResponse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@BaseCeleryApp(name='apps.vadmin.permission.tasks.clear_invalid_captcha')
|
||||
def clear_invalid_captcha():
|
||||
"""
|
||||
清除数据库中废弃失效的验证码
|
||||
:return:
|
||||
"""
|
||||
queryset = CaptchaStore.objects.filter(expiration__lt=datetime.datetime.now())
|
||||
msg = f"成功删除 {queryset.count()} 条失效验证码!"
|
||||
logger.info(msg)
|
||||
queryset.delete()
|
||||
return SuccessResponse(msg=msg)
|
|
@ -7,4 +7,5 @@ from ..models.message_push import MessagePush
|
|||
from ..models.message_push import MessagePushUser
|
||||
from ..models.logininfor import LoginInfor
|
||||
from ..models.operation_log import OperationLog
|
||||
from ..models.celery_log import CeleryLog
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
from django.db.models import CharField, BooleanField, TextField
|
||||
|
||||
from ...op_drf.models import CoreModel
|
||||
|
||||
|
||||
class CeleryLog(CoreModel):
|
||||
name = CharField(max_length=256, verbose_name='任务名称', help_text='任务名称')
|
||||
kwargs = TextField(max_length=1024, verbose_name='执行参数', help_text='运行参数')
|
||||
seconds = CharField(max_length=8, verbose_name='执行时间')
|
||||
state = BooleanField(default=False, verbose_name='运行状态')
|
||||
result = TextField(max_length=10240, verbose_name='任务结果', help_text='任务返回内容')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'celery定时日志'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.creator and self.creator.name}"
|
|
@ -1,17 +1,22 @@
|
|||
"""
|
||||
常用的装饰器以及DRF的装饰器
|
||||
"""
|
||||
import functools
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
import functools
|
||||
from collections import Iterable
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from rest_framework_extensions.settings import extensions_api_settings
|
||||
from django.utils import six
|
||||
from django.utils.decorators import available_attrs
|
||||
from rest_framework.response import Response
|
||||
from rest_framework_extensions.settings import extensions_api_settings
|
||||
|
||||
from application.celery import app
|
||||
from .string_util import bas64_encode_text, bas64_decode_text
|
||||
from ..system.models import CeleryLog
|
||||
|
||||
|
||||
def get_cache(alias=None):
|
||||
|
@ -19,12 +24,46 @@ def get_cache(alias=None):
|
|||
return caches[alias or extensions_api_settings.DEFAULT_USE_CACHE]
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def BaseCeleryApp(name):
|
||||
def wraps(func):
|
||||
@app.task(name=name)
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
obj = CeleryLog()
|
||||
obj.name = str(func.__name__)
|
||||
obj.kwargs = f"*args:{args}\n**kwargs:{kwargs}"
|
||||
start_time = datetime.now()
|
||||
res = None
|
||||
try:
|
||||
res = func(*args, **kwargs)
|
||||
obj.result = str(res)
|
||||
obj.state = True
|
||||
except Exception as exc:
|
||||
obj.state = False
|
||||
obj.result = f"执行失败,错误信息:{exc}"
|
||||
logger.info(f"传入参数:{args, kwargs}")
|
||||
logger.error(f"执行失败,错误信息:{exc}")
|
||||
end_time = datetime.now()
|
||||
seconds = (end_time - start_time).seconds
|
||||
obj.seconds = seconds
|
||||
obj.save()
|
||||
return res
|
||||
|
||||
return wrapper
|
||||
|
||||
return wraps
|
||||
|
||||
|
||||
def print_fun_time(logger=None):
|
||||
"""
|
||||
打印函数执行时间
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
@functools.wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
|
@ -37,15 +76,19 @@ def print_fun_time(logger=None):
|
|||
else:
|
||||
print(f"{func.__name__}耗时:{seconds}秒")
|
||||
return res
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def print_time(logger=None):
|
||||
"""
|
||||
打印函数执行时间
|
||||
:param logger:
|
||||
:return:
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
@functools.wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
|
@ -58,7 +101,9 @@ def print_time(logger=None):
|
|||
else:
|
||||
print(f"{func.__name__}耗时:{seconds}秒")
|
||||
return res
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -68,11 +113,13 @@ def bas64_encode(func):
|
|||
:param func:
|
||||
:return:
|
||||
"""
|
||||
|
||||
def inner(*args, **kwargs):
|
||||
text = func(*args, **kwargs)
|
||||
if isinstance(text, str):
|
||||
text = bas64_encode_text(text)
|
||||
return text
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
|
@ -82,11 +129,13 @@ def bas64_decode(func):
|
|||
:param func:
|
||||
:return:
|
||||
"""
|
||||
|
||||
def inner(*args, **kwargs):
|
||||
text = func(*args, **kwargs)
|
||||
if isinstance(text, str):
|
||||
text = bas64_decode_text(text)
|
||||
return text
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
|
@ -95,6 +144,7 @@ def decode(crypto=""):
|
|||
解密装饰器:BASE64
|
||||
:param crypto: 解密算法名称(忽略大小写)
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
def inner(*args, **kwargs):
|
||||
text = func(*args, **kwargs)
|
||||
|
@ -104,7 +154,9 @@ def decode(crypto=""):
|
|||
else:
|
||||
text = text
|
||||
return text
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -113,11 +165,14 @@ def encode(crypto=""):
|
|||
解密装饰器:BASE64
|
||||
:param crypto: 解密算法名称(忽略大小写)
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
def inner(*args, **kwargs):
|
||||
text = func(*args, **kwargs)
|
||||
return text
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -133,6 +188,7 @@ def envFunction(envs='', execute=True, result=None):
|
|||
pass
|
||||
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
@functools.wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
|
@ -148,7 +204,9 @@ def envFunction(envs='', execute=True, result=None):
|
|||
elif settings.PROJECT_ENV not in environments and not execute:
|
||||
return func(*args, **kwargs)
|
||||
return result
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -161,6 +219,7 @@ def exceptionHandler(logger=None, throw=False, result=None, message=None):
|
|||
:param message: 错误信息
|
||||
:return:
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
@functools.wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
|
@ -174,7 +233,9 @@ def exceptionHandler(logger=None, throw=False, result=None, message=None):
|
|||
if throw:
|
||||
raise e
|
||||
return result
|
||||
|
||||
return inner
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
@ -203,6 +264,7 @@ class CacheResponse(object):
|
|||
|
||||
def __call__(self, func):
|
||||
this = self
|
||||
|
||||
@functools.wraps(func, assigned=available_attrs(func))
|
||||
def inner(self, request, *args, **kwargs):
|
||||
return this.process_cache_response(
|
||||
|
@ -212,6 +274,7 @@ class CacheResponse(object):
|
|||
args=args,
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
return inner
|
||||
|
||||
def process_cache_response(self,
|
||||
|
@ -274,4 +337,3 @@ class CacheResponse(object):
|
|||
|
||||
|
||||
cache_response = CacheResponse
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
asgiref==3.3.1
|
||||
Django==2.2.16
|
||||
django-cors-headers==3.7.0
|
||||
django-celery==3.2.2
|
||||
django_celery_beat==2.2.0
|
||||
django-filter==2.4.0
|
||||
django-ranged-response==0.2.0
|
||||
django-redis==4.12.1
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</el-autocomplete>
|
||||
</el-form-item>
|
||||
<el-form-item :rules="[{ required: true, message: '名称不能为空'}]" prop="name" label="名称:">
|
||||
<el-input v-model="form.name" placeholder="例如: 主机表同步任务" style="width: 400px;"/>
|
||||
<el-input v-model="form.name" placeholder="例如: XXX同步任务" style="width: 400px;"/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="interval" label="任务频率:">
|
||||
<el-select v-model="form.interval" placeholder="请选择任务频率" style="width: 400px;" @change="form.crontab = ''">
|
||||
|
|
Loading…
Reference in New Issue