Skip to content

Commit 14970ef

Browse files
Tylerloggitee-org
authored andcommitted
!55 正式发布v0.0.4版本
1.新增批量删除功能 2.新增智能过滤器(使用方法在群 @q群管家) 3.新增 API日志模块名映射功能 4.新增及兼容定时任务插件功能 5.新增插件安装的自动化数据库迁移 6.优化图片及附件model 7.优化强制初始化时,不删除用户表,只做更新 8.优化验证菜单路由地址必须以/开头 9.优化登录方式配置及initialize 优化 10.修复在获取请求IP时报错bug 11.修复权限管理中部门获取的bug 12.修复错误信息前端提示bug 13.其他优化
2 parents 5ce0780 + 3b2e7d1 commit 14970ef

File tree

32 files changed

+513
-167
lines changed

32 files changed

+513
-167
lines changed

backend/application/celery.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import os
2+
3+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
4+
5+
from django.conf import settings
6+
from celery import platforms
7+
8+
if getattr(settings, 'PLUGINS_LIST', {}).get('dvadmin_tenant_backend', None):
9+
from tenant_schemas_celery.app import CeleryApp as TenantAwareCeleryApp
10+
app = TenantAwareCeleryApp()
11+
else:
12+
from celery import Celery
13+
14+
app = Celery(f"application")
15+
app.config_from_object('django.conf:settings')
16+
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
17+
platforms.C_FORCE_ROOT = True

backend/application/settings.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,13 @@
255255
),
256256
'EXCEPTION_HANDLER': 'dvadmin.utils.exception.CustomExceptionHandler', # 自定义的异常处理
257257
}
258+
# ================================================= #
259+
# ******************** 登录方式配置 ******************** #
260+
# ================================================= #
258261

262+
AUTHENTICATION_BACKENDS = [
263+
'dvadmin.utils.backends.CustomBackend'
264+
]
259265
# ================================================= #
260266
# ****************** simplejwt配置 ***************** #
261267
# ================================================= #
@@ -334,10 +340,15 @@
334340
API_MODEL_MAP = {
335341
"/token/": "登录模块",
336342
"/api/login/": "登录模块",
343+
"/api/plugins_market/plugins/": "插件市场",
337344
}
338345
# 表前缀
339346
TABLE_PREFIX = "dvadmin_"
340347
DJANGO_CELERY_BEAT_TZ_AWARE = False
341348
CELERY_TIMEZONE = 'Asia/Shanghai' # celery 时区问题
349+
# 初始化需要执行的列表,用来初始化后执行
350+
INITIALIZE_LIST = []
351+
INITIALIZE_RESET_LIST = []
342352
# 导入租户数据
353+
SHARED_APPS = []
343354
from plugins import *

backend/application/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def get_classes(arg):
9191
url_prefix = getattr(getattr(apps, app_config), "url_prefix")
9292
if url_prefix:
9393
url_prefix = url_prefix[0] + '/'
94+
9495
# 注册路由
9596
try:
9697
url = [
@@ -99,5 +100,7 @@ def get_classes(arg):
99100
urlpatterns += url
100101
print(f"【{plugins_values.get('name', None)}】路由导入成功")
101102
except Exception as e:
103+
print(f"【{plugins_values.get('name', None)}】路由导入失败:")
104+
print(e)
102105
pass
103106
""")

backend/dvadmin/system/initialize.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ def init_menu(self):
8080
{"id": "805390f3-a6e6-411e-9798-eebd34b76204", "name": "附件管理", "sort": 7, "web_path": "",
8181
"icon": "envelope", "parent_id": "54f769b0-3dff-416c-8102-e55ec44827cc", },
8282
{"id": "ac956a17-87d3-4b61-8f72-44a97b9fbcd1", "name": "图片管理", "sort": 1, "web_path": "/img",
83-
"icon": "file-image-o", "parent_id": "805390f3-a6e6-411e-9798-eebd34b76204", "component": "system/fileList/img/index",
83+
"icon": "file-image-o", "parent_id": "805390f3-a6e6-411e-9798-eebd34b76204",
84+
"component": "system/fileList/img/index",
8485
"component_name": "imgs"},
8586
{"id": "28723f68-e470-493a-bbe7-7b759fe26674", "name": "文件管理", "sort": 2, "web_path": "/file",
86-
"icon": "file-excel-o", "parent_id": "805390f3-a6e6-411e-9798-eebd34b76204", "component": "system/fileList/file/index",
87+
"icon": "file-excel-o", "parent_id": "805390f3-a6e6-411e-9798-eebd34b76204",
88+
"component": "system/fileList/file/index",
8789
"component_name": "file"},
8890
]
8991
self.save(Menu, self.menu_data, "菜单表")
@@ -253,7 +255,7 @@ def init_users(self):
253255
"role": ["36001d1a-1b3e-4413-bdfe-b3bc04375f46"],
254256
},
255257
]
256-
self.save(Users, data, "用户表")
258+
self.save(Users, data, "用户表", no_reset=True)
257259

258260
def run(self):
259261
self.init_dept()

backend/dvadmin/system/models.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import hashlib
2+
import os
23

34
from django.contrib.auth.models import AbstractUser
45
from django.db import models
@@ -35,7 +36,7 @@ class Meta:
3536
db_table = table_prefix + "system_users"
3637
verbose_name = '用户表'
3738
verbose_name_plural = verbose_name
38-
ordering = ('create_datetime',)
39+
ordering = ('-create_datetime',)
3940

4041

4142
class Post(CoreModel):
@@ -126,6 +127,7 @@ class Menu(CoreModel):
126127
(1, "是"),
127128
)
128129
is_link = models.IntegerField(choices=ISLINK_CHOICES, default=0, verbose_name="是否外链", help_text="是否外链")
130+
is_catalog = models.IntegerField(choices=ISLINK_CHOICES,default=0,verbose_name="是否目录",help_text="是否目录")
129131
web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
130132
component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
131133
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True, help_text="组件名称")
@@ -211,9 +213,24 @@ class Meta:
211213
ordering = ('-create_datetime',)
212214

213215

216+
def media_img_name(instance, filename):
217+
h = instance.md5sum
218+
basename, ext = os.path.splitext(filename)
219+
return os.path.join('media/imgs', h[0:1], h[1:2], h + ext.lower())
220+
221+
214222
class ImgList(CoreModel):
215223
name = models.CharField(max_length=50, null=True, blank=True, verbose_name="名称", help_text="名称")
216-
url = models.ImageField(upload_to='media/imgs/%Y%m%d/')
224+
url = models.ImageField(upload_to=media_img_name)
225+
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
226+
227+
def save(self, *args, **kwargs):
228+
if not self.md5sum: # file is new
229+
md5 = hashlib.md5()
230+
for chunk in self.url.chunks():
231+
md5.update(chunk)
232+
self.md5sum = md5.hexdigest()
233+
super(ImgList, self).save(*args, **kwargs)
217234

218235
class Meta:
219236
db_table = table_prefix + 'img_list'
@@ -222,14 +239,27 @@ class Meta:
222239
ordering = ('-create_datetime',)
223240

224241

242+
def media_file_name(instance, filename):
243+
h = instance.md5sum
244+
basename, ext = os.path.splitext(filename)
245+
return os.path.join('media/files', h[0:1], h[1:2], h + ext.lower())
246+
247+
225248
class FileList(CoreModel):
226249
name = models.CharField(max_length=50, null=True, blank=True, verbose_name="名称", help_text="名称")
227-
url = models.FileField(upload_to='media/files/%Y%m%d/')
250+
url = models.FileField(upload_to=media_file_name)
251+
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
252+
253+
def save(self, *args, **kwargs):
254+
if not self.md5sum: # file is new
255+
md5 = hashlib.md5()
256+
for chunk in self.url.chunks():
257+
md5.update(chunk)
258+
self.md5sum = md5.hexdigest()
259+
super(FileList, self).save(*args, **kwargs)
228260

229261
class Meta:
230262
db_table = table_prefix + 'file_list'
231263
verbose_name = '文件管理'
232264
verbose_name_plural = verbose_name
233265
ordering = ('-create_datetime',)
234-
235-

backend/dvadmin/system/urls.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,11 @@
3333
system_url.register(r'file',FileViewSet)
3434

3535

36+
3637
urlpatterns = [
3738
re_path('role/role_id_to_menu/(?P<pk>.*?)/', RoleViewSet.as_view({'get': 'roleId_to_menu'})),
38-
re_path('menu/web_router/', MenuViewSet.as_view({'get': 'web_router'})),
39+
path('menu/web_router/', MenuViewSet.as_view({'get': 'web_router'})),
3940
path('user/user_info/',UserViewSet.as_view({'get':'user_info','put':'update_user_info'})),
4041
re_path('user/change_password/(?P<pk>.*?)/',UserViewSet.as_view({'put':'change_password'})),
41-
42-
4342
]
4443
urlpatterns += system_url.urls

backend/dvadmin/system/views/file_list.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@
1414

1515

1616
class FileSerializer(CustomModelSerializer):
17-
img = serializers.SerializerMethodField(read_only=True)
17+
url = serializers.SerializerMethodField(read_only=True)
1818

19-
def get_img(self,instance):
19+
def get_url(self, instance):
2020
return str(instance.url)
2121

2222
class Meta:
2323
model = FileList
2424
fields = "__all__"
2525

26-
def create(self,validated_data):
27-
validated_data['name'] = str(validated_data.get('url'))
28-
return FileList.objects.create(**validated_data)
26+
def create(self, validated_data):
27+
validated_data['name'] = str(self.initial_data.get('url'))
28+
validated_data['url'] = self.initial_data.get('url')
29+
return super().create(validated_data)
2930

3031

3132
class FileViewSet(CustomModelViewSet):
@@ -39,4 +40,4 @@ class FileViewSet(CustomModelViewSet):
3940
"""
4041
queryset = FileList.objects.all()
4142
serializer_class = FileSerializer
42-
filter_fields = ['name',]
43+
filter_fields = ['name', ]

backend/dvadmin/system/views/img_list.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
class ImgSerializer(CustomModelSerializer):
1717
img = serializers.SerializerMethodField(read_only=True)
1818

19-
def get_img(self,instance):
19+
def get_img(self, instance):
2020
return str(instance.url)
2121

2222
class Meta:
2323
model = ImgList
2424
fields = "__all__"
2525

26-
def create(self,validated_data):
26+
def create(self, validated_data):
2727
validated_data['name'] = str(validated_data.get('url'))
28-
return ImgList.objects.create(**validated_data)
28+
return super().create(validated_data)
2929

3030

3131
class ImgViewSet(CustomModelViewSet):
@@ -39,4 +39,4 @@ class ImgViewSet(CustomModelViewSet):
3939
"""
4040
queryset = ImgList.objects.all()
4141
serializer_class = ImgSerializer
42-
filter_fields = ['name',]
42+
filter_fields = ['name', ]

backend/dvadmin/system/views/login.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,13 @@ def validate_captcha(self, captcha):
6969
id=self.initial_data['captchaKey']).first()
7070
five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
7171
if self.image_code and five_minute_ago > self.image_code.expiration:
72+
self.image_code and self.image_code.delete()
7273
raise CustomValidationError('验证码过期')
7374
else:
7475
if self.image_code and (self.image_code.response == captcha or self.image_code.challenge == captcha):
75-
pass
76+
self.image_code and self.image_code.delete()
7677
else:
77-
self.image_code.delete()
78+
self.image_code and self.image_code.delete()
7879
raise CustomValidationError("图片验证码错误")
7980

8081
def validate(self, attrs):
@@ -95,7 +96,6 @@ def validate(self, attrs):
9596
"data": data
9697
}
9798
else:
98-
self.image_code.delete()
9999
result = {
100100
"code": 4000,
101101
"msg": "账号/密码不正确",
@@ -113,6 +113,7 @@ class LoginView(TokenObtainPairView):
113113

114114

115115
class ApiLoginSerializer(CustomModelSerializer):
116+
"""接口文档登录-序列化器"""
116117
username = serializers.CharField()
117118
password = serializers.CharField()
118119

@@ -125,6 +126,8 @@ class Meta:
125126
class ApiLogin(APIView):
126127
"""接口文档的登录接口"""
127128
serializer_class = ApiLoginSerializer
129+
authentication_classes = []
130+
permission_classes = []
128131

129132
def post(self, request):
130133
username = request.data.get('username')
@@ -134,4 +137,4 @@ def post(self, request):
134137
login(request,user_obj)
135138
return redirect('/')
136139
else:
137-
return ErrorResponse(msg="账号/密码错误")
140+
return ErrorResponse(msg="账号/密码错误")

backend/dvadmin/utils/backends.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import logging
2+
3+
from django.contrib.auth import get_user_model
4+
from django.contrib.auth.backends import ModelBackend
5+
from django.utils import timezone
6+
7+
logger = logging.getLogger(__name__)
8+
UserModel = get_user_model()
9+
10+
11+
class CustomBackend(ModelBackend):
12+
"""
13+
Django原生认证方式
14+
"""
15+
16+
def authenticate(self, request, username=None, password=None, **kwargs):
17+
msg = '%s 正在使用本地登录...' % username
18+
logger.info(msg)
19+
if username is None:
20+
username = kwargs.get(UserModel.USERNAME_FIELD)
21+
try:
22+
user = UserModel._default_manager.get_by_natural_key(username)
23+
except UserModel.DoesNotExist:
24+
UserModel().set_password(password)
25+
else:
26+
if user.check_password(password) and self.user_can_authenticate(user):
27+
user.last_login = timezone.now()
28+
user.save()
29+
return user

0 commit comments

Comments
 (0)