下载中心模块和异步导出功能
This commit is contained in:
@@ -672,6 +672,53 @@
|
||||
"model": "ApiWhiteList"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "下载中心",
|
||||
"icon": "ele-Download",
|
||||
"sort": 9,
|
||||
"is_link": false,
|
||||
"is_catalog": false,
|
||||
"web_path": "/downloadCenter",
|
||||
"component": "system/downloadCenter/index",
|
||||
"component_name": "downloadCenter",
|
||||
"status": true,
|
||||
"cache": false,
|
||||
"visible": true,
|
||||
"parent": 277,
|
||||
"children": [],
|
||||
"menu_button": [
|
||||
{
|
||||
"name": "查询",
|
||||
"value": "Search",
|
||||
"api": "/api/system/downloadCenter/",
|
||||
"method": 0
|
||||
},
|
||||
{
|
||||
"name": "详情",
|
||||
"value": "Retrieve",
|
||||
"api": "/api/system/downloadCenter/{id}/",
|
||||
"method": 0
|
||||
},
|
||||
{
|
||||
"name": "新增",
|
||||
"value": "Create",
|
||||
"api": "/api/system/downloadCenter/",
|
||||
"method": 1
|
||||
},
|
||||
{
|
||||
"name": "编辑",
|
||||
"value": "Update",
|
||||
"api": "/api/system/downloadCenter/{id}/",
|
||||
"method": 2
|
||||
},
|
||||
{
|
||||
"name": "删除",
|
||||
"value": "Delete",
|
||||
"api": "/api/system/downloadCenter/{id}/",
|
||||
"method": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"menu_button": [],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import hashlib
|
||||
import os
|
||||
from time import time
|
||||
from pathlib import PurePosixPath
|
||||
|
||||
from django.contrib.auth.models import AbstractUser, UserManager
|
||||
from django.db import models
|
||||
@@ -595,3 +597,41 @@ class MessageCenterTargetUser(CoreModel):
|
||||
db_table = table_prefix + "message_center_target_user"
|
||||
verbose_name = "消息中心目标用户表"
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
def media_file_name_downloadcenter(instance:'DownloadCenter', filename):
|
||||
h = instance.md5sum
|
||||
basename, ext = os.path.splitext(filename)
|
||||
return PurePosixPath("files", "dlct", h[:1], h[1:2], basename + '-' + str(time()).replace('.', '') + ext.lower())
|
||||
|
||||
|
||||
class DownloadCenter(CoreModel):
|
||||
TASK_STATUS_CHOICES = [
|
||||
(0, '任务已创建'),
|
||||
(1, '任务进行中'),
|
||||
(2, '任务完成'),
|
||||
(3, '任务失败'),
|
||||
]
|
||||
task_name = models.CharField(max_length=255, verbose_name="任务名称", help_text="任务名称")
|
||||
task_status = models.SmallIntegerField(default=0, choices=TASK_STATUS_CHOICES, verbose_name='是否可下载', help_text='是否可下载')
|
||||
file_name = models.CharField(max_length=255, null=True, blank=True, verbose_name="文件名", help_text="文件名")
|
||||
url = models.FileField(upload_to=media_file_name_downloadcenter, null=True, blank=True)
|
||||
size = models.BigIntegerField(default=0, verbose_name="文件大小", help_text="文件大小")
|
||||
md5sum = models.CharField(max_length=36, null=True, blank=True, verbose_name="文件md5", help_text="文件md5")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.url:
|
||||
if not self.md5sum: # file is new
|
||||
md5 = hashlib.md5()
|
||||
for chunk in self.url.chunks():
|
||||
md5.update(chunk)
|
||||
self.md5sum = md5.hexdigest()
|
||||
if not self.size:
|
||||
self.size = self.url.size
|
||||
super(DownloadCenter, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
db_table = table_prefix + "download_center"
|
||||
verbose_name = "下载中心"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ("-create_datetime",)
|
||||
|
||||
107
backend/dvadmin/system/tasks.py
Normal file
107
backend/dvadmin/system/tasks.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from hashlib import md5
|
||||
from io import BytesIO
|
||||
from datetime import datetime
|
||||
from time import sleep
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.worksheet.table import Table, TableStyleInfo
|
||||
from openpyxl.utils import get_column_letter
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from application.celery import app
|
||||
from dvadmin.system.models import DownloadCenter
|
||||
|
||||
def is_number(num):
|
||||
try:
|
||||
float(num)
|
||||
return True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import unicodedata
|
||||
unicodedata.numeric(num)
|
||||
return True
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return False
|
||||
|
||||
def get_string_len(string):
|
||||
"""
|
||||
获取字符串最大长度
|
||||
:param string:
|
||||
:return:
|
||||
"""
|
||||
length = 4
|
||||
if string is None:
|
||||
return length
|
||||
if is_number(string):
|
||||
return length
|
||||
for char in string:
|
||||
length += 2.1 if ord(char) > 256 else 1
|
||||
return round(length, 1) if length <= 50 else 50
|
||||
|
||||
@app.task
|
||||
def async_export_data(data: list, filename: str, dcid: int, export_field_label: dict):
|
||||
instance = DownloadCenter.objects.get(pk=dcid)
|
||||
instance.task_status = 1
|
||||
instance.save()
|
||||
sleep(2)
|
||||
try:
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
header_data = ["序号", *export_field_label.values()]
|
||||
hidden_header = ["#", *export_field_label.keys()]
|
||||
df_len_max = [get_string_len(ele) for ele in header_data]
|
||||
row = get_column_letter(len(export_field_label) + 1)
|
||||
column = 1
|
||||
ws.append(header_data)
|
||||
for index, results in enumerate(data):
|
||||
results_list = []
|
||||
for h_index, h_item in enumerate(hidden_header):
|
||||
for key, val in results.items():
|
||||
if key == h_item:
|
||||
if val is None or val == "":
|
||||
results_list.append("")
|
||||
elif isinstance(val, datetime):
|
||||
val = val.strftime("%Y-%m-%d %H:%M:%S")
|
||||
results_list.append(val)
|
||||
else:
|
||||
results_list.append(val)
|
||||
# 计算最大列宽度
|
||||
result_column_width = get_string_len(val)
|
||||
if h_index != 0 and result_column_width > df_len_max[h_index]:
|
||||
df_len_max[h_index] = result_column_width
|
||||
ws.append([index + 1, *results_list])
|
||||
column += 1
|
||||
# 更新列宽
|
||||
for index, width in enumerate(df_len_max):
|
||||
ws.column_dimensions[get_column_letter(index + 1)].width = width
|
||||
tab = Table(displayName="Table", ref=f"A1:{row}{column}") # 名称管理器
|
||||
style = TableStyleInfo(
|
||||
name="TableStyleLight11",
|
||||
showFirstColumn=True,
|
||||
showLastColumn=True,
|
||||
showRowStripes=True,
|
||||
showColumnStripes=True,
|
||||
)
|
||||
tab.tableStyleInfo = style
|
||||
ws.add_table(tab)
|
||||
stream = BytesIO()
|
||||
wb.save(stream)
|
||||
stream.seek(0)
|
||||
s = md5()
|
||||
while True:
|
||||
chunk = stream.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
s.update(chunk)
|
||||
stream.seek(0)
|
||||
instance.md5sum = s.hexdigest()
|
||||
instance.file_name = filename
|
||||
instance.url.save(filename, ContentFile(stream.read()))
|
||||
instance.task_status = 2
|
||||
except Exception as e:
|
||||
instance.task_status = 3
|
||||
instance.description = str(e)[:250]
|
||||
instance.save()
|
||||
@@ -18,6 +18,7 @@ from dvadmin.system.views.role_menu_button_permission import RoleMenuButtonPermi
|
||||
from dvadmin.system.views.system_config import SystemConfigViewSet
|
||||
from dvadmin.system.views.user import UserViewSet
|
||||
from dvadmin.system.views.menu_field import MenuFieldViewSet
|
||||
from dvadmin.system.views.download_center import DownloadCenterViewSet
|
||||
|
||||
system_url = routers.SimpleRouter()
|
||||
system_url.register(r'menu', MenuViewSet)
|
||||
@@ -36,6 +37,7 @@ system_url.register(r'role_menu_button_permission', RoleMenuButtonPermissionView
|
||||
system_url.register(r'role_menu_permission', RoleMenuPermissionViewSet)
|
||||
system_url.register(r'column', MenuFieldViewSet)
|
||||
system_url.register(r'login_log', LoginLogViewSet)
|
||||
system_url.register(r'download_center', DownloadCenterViewSet)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
46
backend/dvadmin/system/views/download_center.py
Normal file
46
backend/dvadmin/system/views/download_center.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from rest_framework import serializers
|
||||
from django.conf import settings
|
||||
from django_filters.rest_framework import FilterSet, CharFilter
|
||||
|
||||
from dvadmin.utils.serializers import CustomModelSerializer
|
||||
from dvadmin.utils.viewset import CustomModelViewSet
|
||||
from dvadmin.system.models import DownloadCenter
|
||||
|
||||
|
||||
class DownloadCenterSerializer(CustomModelSerializer):
|
||||
url = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
def get_url(self, instance):
|
||||
if self.request.query_params.get('prefix'):
|
||||
if settings.ENVIRONMENT in ['local']:
|
||||
prefix = 'http://127.0.0.1:8000'
|
||||
elif settings.ENVIRONMENT in ['test']:
|
||||
prefix = 'http://{host}/api'.format(host=self.request.get_host())
|
||||
else:
|
||||
prefix = 'https://{host}/api'.format(host=self.request.get_host())
|
||||
return (f'{prefix}/media/{str(instance.url)}')
|
||||
return f'media/{str(instance.url)}'
|
||||
|
||||
class Meta:
|
||||
model = DownloadCenter
|
||||
fields = "__all__"
|
||||
read_only_fields = ["id"]
|
||||
|
||||
|
||||
class DownloadCenterFilterSet(FilterSet):
|
||||
task_name = CharFilter(field_name='task_name', lookup_expr='icontains')
|
||||
file_name = CharFilter(field_name='file_name', lookup_expr='icontains')
|
||||
|
||||
class Meta:
|
||||
model = DownloadCenter
|
||||
fields = ['task_status', 'task_name', 'file_name']
|
||||
|
||||
|
||||
class DownloadCenterViewSet(CustomModelViewSet):
|
||||
queryset = DownloadCenter.objects.all()
|
||||
serializer_class = DownloadCenterSerializer
|
||||
filter_class = DownloadCenterFilterSet
|
||||
permission_classes = []
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(creator=self.request.user)
|
||||
Reference in New Issue
Block a user