diff --git a/backend/application/dispatch.py b/backend/application/dispatch.py
index 3bd364c..101b3ab 100644
--- a/backend/application/dispatch.py
+++ b/backend/application/dispatch.py
@@ -2,6 +2,10 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import connection
+from django.core.cache import cache
+from dvadmin.utils.validator import CustomValidationError
+
+dispatch_db_type = getattr(settings, 'DISPATCH_DB_TYPE', 'memory') # redis
def is_tenants_mode():
@@ -68,6 +72,9 @@ def init_dictionary():
:return:
"""
try:
+ if dispatch_db_type == 'redis':
+ cache.set(f"init_dictionary", _get_all_dictionary())
+ return
if is_tenants_mode():
from django_tenants.utils import tenant_context, get_tenant_model
@@ -88,7 +95,9 @@ def init_system_config():
:return:
"""
try:
-
+ if dispatch_db_type == 'redis':
+ cache.set(f"init_system_config", _get_all_system_config())
+ return
if is_tenants_mode():
from django_tenants.utils import tenant_context, get_tenant_model
@@ -107,6 +116,9 @@ def refresh_dictionary():
刷新字典配置
:return:
"""
+ if dispatch_db_type == 'redis':
+ cache.set(f"init_dictionary", _get_all_dictionary())
+ return
if is_tenants_mode():
from django_tenants.utils import tenant_context, get_tenant_model
@@ -122,6 +134,9 @@ def refresh_system_config():
刷新系统配置
:return:
"""
+ if dispatch_db_type == 'redis':
+ cache.set(f"init_system_config", _get_all_system_config())
+ return
if is_tenants_mode():
from django_tenants.utils import tenant_context, get_tenant_model
@@ -141,6 +156,11 @@ def get_dictionary_config(schema_name=None):
:param schema_name: 对应字典配置的租户schema_name值
:return:
"""
+ if dispatch_db_type == 'redis':
+ init_dictionary_data = cache.get(f"init_dictionary")
+ if not init_dictionary_data:
+ refresh_dictionary()
+ return cache.get(f"init_dictionary") or {}
if not settings.DICTIONARY_CONFIG:
refresh_dictionary()
if is_tenants_mode():
@@ -157,6 +177,12 @@ def get_dictionary_values(key, schema_name=None):
:param schema_name: 对应字典配置的租户schema_name值
:return:
"""
+ if dispatch_db_type == 'redis':
+ dictionary_config = cache.get(f"init_dictionary")
+ if not dictionary_config:
+ refresh_dictionary()
+ dictionary_config = cache.get(f"init_dictionary")
+ return dictionary_config.get(key)
dictionary_config = get_dictionary_config(schema_name)
return dictionary_config.get(key)
@@ -169,8 +195,8 @@ def get_dictionary_label(key, name, schema_name=None):
:param schema_name: 对应字典配置的租户schema_name值
:return:
"""
- children = get_dictionary_values(key, schema_name) or []
- for ele in children:
+ res = get_dictionary_values(key, schema_name) or []
+ for ele in res.get('children'):
if ele.get("value") == str(name):
return ele.get("label")
return ""
@@ -187,6 +213,11 @@ def get_system_config(schema_name=None):
:param schema_name: 对应字典配置的租户schema_name值
:return:
"""
+ if dispatch_db_type == 'redis':
+ init_dictionary_data = cache.get(f"init_system_config")
+ if not init_dictionary_data:
+ refresh_system_config()
+ return cache.get(f"init_system_config") or {}
if not settings.SYSTEM_CONFIG:
refresh_system_config()
if is_tenants_mode():
@@ -203,10 +234,32 @@ def get_system_config_values(key, schema_name=None):
:param schema_name: 对应系统配置的租户schema_name值
:return:
"""
+ if dispatch_db_type == 'redis':
+ system_config = cache.get(f"init_system_config")
+ if not system_config:
+ refresh_system_config()
+ system_config = cache.get(f"init_system_config")
+ return system_config.get(key)
system_config = get_system_config(schema_name)
return system_config.get(key)
+def get_system_config_values_to_dict(key, schema_name=None):
+ """
+ 获取系统配置数据并转换为字典 **仅限于数组类型系统配置
+ :param key: 对应系统配置的key值(字典编号)
+ :param schema_name: 对应系统配置的租户schema_name值
+ :return:
+ """
+ values_dict = {}
+ config_values = get_system_config_values(key, schema_name)
+ if not isinstance(config_values, list):
+ raise CustomValidationError("该方式仅限于数组类型系统配置")
+ for ele in get_system_config_values(key, schema_name):
+ values_dict[ele.get('key')] = ele.get('value')
+ return values_dict
+
+
def get_system_config_label(key, name, schema_name=None):
"""
获取获取系统配置label值
diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py
index d9a4274..5d500b7 100644
--- a/backend/dvadmin/system/models.py
+++ b/backend/dvadmin/system/models.py
@@ -73,6 +73,7 @@ class Users(CoreModel, AbstractUser):
help_text="关联部门",
)
login_error_count = models.IntegerField(default=0, verbose_name="登录错误次数", help_text="登录错误次数")
+ pwd_change_count = models.IntegerField(default=0,blank=True, verbose_name="密码修改次数", help_text="密码修改次数")
objects = CustomUserManager()
def set_password(self, raw_password):
@@ -407,6 +408,18 @@ class FileList(CoreModel):
mime_type = models.CharField(max_length=100, blank=True, verbose_name="Mime类型", help_text="Mime类型")
size = models.CharField(max_length=36, blank=True, verbose_name="文件大小", help_text="文件大小")
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
+ UPLOAD_METHOD_CHOIDES = (
+ (0, '默认上传'),
+ (1, '文件选择器上传'),
+ )
+ upload_method = models.SmallIntegerField(default=0, blank=True, null=True, choices=UPLOAD_METHOD_CHOIDES, verbose_name='上传方式', help_text='上传方式')
+ FILE_TYPE_CHOIDES = (
+ (0, '图片'),
+ (1, '视频'),
+ (2, '音频'),
+ (3, '其他'),
+ )
+ file_type = models.SmallIntegerField(default=3, choices=FILE_TYPE_CHOIDES, blank=True, null=True, verbose_name='文件类型', help_text='文件类型')
def save(self, *args, **kwargs):
if not self.md5sum: # file is new
diff --git a/backend/dvadmin/system/views/file_list.py b/backend/dvadmin/system/views/file_list.py
index c595699..eb97270 100644
--- a/backend/dvadmin/system/views/file_list.py
+++ b/backend/dvadmin/system/views/file_list.py
@@ -1,12 +1,15 @@
import hashlib
import mimetypes
+import django_filters
+from django.conf import settings
+from django.db import connection
from rest_framework import serializers
from rest_framework.decorators import action
from application import dispatch
from dvadmin.system.models import FileList
-from dvadmin.utils.json_response import DetailResponse
+from dvadmin.utils.json_response import DetailResponse, SuccessResponse
from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.viewset import CustomModelViewSet
@@ -15,8 +18,21 @@ class FileSerializer(CustomModelSerializer):
url = serializers.SerializerMethodField(read_only=True)
def get_url(self, instance):
- base_url = f"{self.request.scheme}://{self.request.get_host()}/"
- return base_url + (instance.file_url or (f'media/{str(instance.url)}'))
+ 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())
+ if instance.file_url:
+ return instance.file_url if instance.file_url.startswith('http') else f"{prefix}/{instance.file_url}"
+ return (f'{prefix}/media/{str(instance.url)}')
+ return instance.file_url or (f'media/{str(instance.url)}')
+
+ class Meta:
+ model = FileList
+ fields = "__all__"
class Meta:
model = FileList
@@ -35,6 +51,8 @@ class FileSerializer(CustomModelSerializer):
validated_data['md5sum'] = md5.hexdigest()
validated_data['engine'] = file_engine
validated_data['mime_type'] = file.content_type
+ ft = {'image':0,'video':1,'audio':2}.get(file.content_type.split('/')[0], None)
+ validated_data['file_type'] = 3 if ft is None else ft
if file_backup:
validated_data['url'] = file
if file_engine == 'oss':
@@ -64,6 +82,22 @@ class FileSerializer(CustomModelSerializer):
return super().create(validated_data)
+class FileAllSerializer(CustomModelSerializer):
+
+ class Meta:
+ model = FileList
+ fields = ['id', 'name']
+
+
+class FileFilter(django_filters.FilterSet):
+ name = django_filters.CharFilter(field_name="name", lookup_expr="icontains", help_text="文件名")
+ mime_type = django_filters.CharFilter(field_name="mime_type", lookup_expr="icontains", help_text="文件类型")
+
+ class Meta:
+ model = FileList
+ fields = ['name', 'mime_type', 'upload_method', 'file_type']
+
+
class FileViewSet(CustomModelViewSet):
"""
文件管理接口
@@ -75,5 +109,22 @@ class FileViewSet(CustomModelViewSet):
"""
queryset = FileList.objects.all()
serializer_class = FileSerializer
- filter_fields = ['name', ]
- permission_classes = []
\ No newline at end of file
+ filter_class = FileFilter
+ permission_classes = []
+
+ @action(methods=['GET'], detail=False)
+ def get_all(self, request):
+ data1 = self.get_serializer(self.get_queryset(), many=True).data
+ data2 = []
+ if dispatch.is_tenants_mode():
+ from django_tenants.utils import schema_context
+ with schema_context('public'):
+ data2 = self.get_serializer(FileList.objects.all(), many=True).data
+ return DetailResponse(data=data2+data1)
+
+ def list(self, request, *args, **kwargs):
+ if self.request.query_params.get('system', 'False') == 'True' and dispatch.is_tenants_mode():
+ from django_tenants.utils import schema_context
+ with schema_context('public'):
+ return super().list(request, *args, **kwargs)
+ return super().list(request, *args, **kwargs)
diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py
index 1996906..3b1209d 100644
--- a/backend/dvadmin/system/views/login.py
+++ b/backend/dvadmin/system/views/login.py
@@ -4,12 +4,15 @@ from datetime import datetime, timedelta
from captcha.views import CaptchaStore, captcha_image
from django.contrib import auth
from django.contrib.auth import login
+from django.contrib.auth.hashers import check_password, make_password
from django.db.models import Q
from django.shortcuts import redirect
from django.utils.translation import gettext_lazy as _
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import serializers
+from rest_framework.decorators import action
+from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
@@ -97,16 +100,17 @@ class LoginSerializer(TokenObtainPairSerializer):
# 必须重置用户名为username,否则使用邮箱手机号登录会提示密码错误
attrs['username'] = user.username
data = super().validate(attrs)
+ data["username"] = self.user.username
data["name"] = self.user.name
data["userId"] = self.user.id
data["avatar"] = self.user.avatar
data['user_type'] = self.user.user_type
+ data['pwd_change_count'] = self.user.pwd_change_count
dept = getattr(self.user, 'dept', None)
if dept:
data['dept_info'] = {
'dept_id': dept.id,
'dept_name': dept.name,
-
}
role = getattr(self.user, 'role', None)
if role:
diff --git a/backend/dvadmin/system/views/menu.py b/backend/dvadmin/system/views/menu.py
index c0c6b65..33ed979 100644
--- a/backend/dvadmin/system/views/menu.py
+++ b/backend/dvadmin/system/views/menu.py
@@ -120,11 +120,11 @@ class MenuViewSet(CustomModelViewSet):
"""用于前端获取当前角色的路由"""
user = request.user
if user.is_superuser:
- queryset = self.queryset.filter(status=1).order_by("id")
+ queryset = self.queryset.filter(status=1).order_by("sort")
else:
role_list = user.role.values_list('id', flat=True)
menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id', flat=True)
- queryset = Menu.objects.filter(id__in=menu_list).order_by("id")
+ queryset = Menu.objects.filter(id__in=menu_list).order_by("sort")
serializer = WebRouterSerializer(queryset, many=True, request=request)
data = serializer.data
return SuccessResponse(data=data, total=len(data), msg="获取成功")
diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py
index 68de06b..16fcbe9 100644
--- a/backend/dvadmin/system/views/user.py
+++ b/backend/dvadmin/system/views/user.py
@@ -286,6 +286,7 @@ class UserViewSet(CustomModelViewSet):
"dept": user.dept_id,
"is_superuser": user.is_superuser,
"role": user.role.values_list('id', flat=True),
+ "pwd_change_count":user.pwd_change_count
}
if hasattr(connection, 'tenant'):
result['tenant_id'] = connection.tenant and connection.tenant.id
@@ -319,7 +320,6 @@ class UserViewSet(CustomModelViewSet):
"""密码修改"""
data = request.data
old_pwd = data.get("oldPassword")
- print(old_pwd)
new_pwd = data.get("newPassword")
new_pwd2 = data.get("newPassword2")
if old_pwd is None or new_pwd is None or new_pwd2 is None:
@@ -335,12 +335,28 @@ class UserViewSet(CustomModelViewSet):
old_pwd_md5 = hashlib.md5(old_pwd_md5.encode(encoding='UTF-8')).hexdigest()
verify_password = check_password(str(old_pwd_md5), request.user.password)
if verify_password:
- request.user.password = make_password(hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest())
+ # request.user.password = make_password(hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest())
+ request.user.password = make_password(new_pwd)
+ request.user.pwd_change_count += 1
request.user.save()
return DetailResponse(data=None, msg="修改成功")
else:
return ErrorResponse(msg="旧密码不正确")
+ @action(methods=["post"], detail=False, permission_classes=[IsAuthenticated])
+ def login_change_password(self, request, *args, **kwargs):
+ """初次登录进行密码修改"""
+ data = request.data
+ new_pwd = data.get("password")
+ new_pwd2 = data.get("password_regain")
+ if new_pwd != new_pwd2:
+ return ErrorResponse(msg="两次密码不匹配")
+ else:
+ request.user.password = make_password(hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest())
+ request.user.pwd_change_count += 1
+ request.user.save()
+ return DetailResponse(data=None, msg="修改成功")
+
@action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated])
def reset_to_default_password(self, request,pk):
"""恢复默认密码"""
diff --git a/web/src/assets/login-bg.png b/web/src/assets/login-bg.png
new file mode 100644
index 0000000..9fd3aa4
Binary files /dev/null and b/web/src/assets/login-bg.png differ
diff --git a/web/src/components/fileSelector/fileItem.vue b/web/src/components/fileSelector/fileItem.vue
new file mode 100644
index 0000000..73ef6f0
--- /dev/null
+++ b/web/src/components/fileSelector/fileItem.vue
@@ -0,0 +1,84 @@
+
+