This commit is contained in:
jayhgq 2025-10-20 21:30:27 +08:00
commit 345da7ed48
393 changed files with 29839 additions and 0 deletions

5
application/__init__.py Normal file
View File

@ -0,0 +1,5 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
__all__ = ('celery_app',)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

21
application/asgi.py Normal file
View File

@ -0,0 +1,21 @@
"""
ASGI config for application project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""
import os
from channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
http_application = get_asgi_application()
application = ProtocolTypeRouter({
"http": http_application,
})

51
application/celery.py Normal file
View File

@ -0,0 +1,51 @@
import functools
import os
from celery.signals import task_postrun
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
from django.conf import settings
from celery import platforms
# 租户模式
if "django_tenants" in settings.INSTALLED_APPS:
from tenant_schemas_celery.app import CeleryApp as TenantAwareCeleryApp
app = TenantAwareCeleryApp()
else:
from celery import Celery
app = Celery(f"application")
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
platforms.C_FORCE_ROOT = True
def retry_base_task_error():
"""
celery 失败重试装饰器
:return:
"""
def wraps(func):
@app.task(bind=True, retry_delay=180, max_retries=3)
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as exc:
raise self.retry(exc=exc)
return wrapper
return wraps
@task_postrun.connect
def add_periodic_task_name(sender, task_id, task, args, kwargs, **extras):
periodic_task_name = kwargs.get('periodic_task_name')
if periodic_task_name:
from django_celery_results.models import TaskResult
# 更新 TaskResult 表中的 periodic_task_name 字段
TaskResult.objects.filter(task_id=task_id).update(periodic_task_name=periodic_task_name)

275
application/dispatch.py Normal file
View File

@ -0,0 +1,275 @@
#!/usr/bin/env python
# -*- 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():
"""
判断是否为租户模式
:return:
"""
return hasattr(connection, "tenant") and connection.tenant.schema_name
# ================================================= #
# ******************** 初始化 ******************** #
# ================================================= #
def _get_all_dictionary():
from dvadmin.system.models import Dictionary
queryset = Dictionary.objects.filter(status=True, is_value=False)
data = []
for instance in queryset:
data.append(
{
"id": instance.id,
"value": instance.value,
"children": list(
Dictionary.objects.filter(parent=instance.id)
.filter(status=1)
.values("label", "value", "type", "color")
),
}
)
return {ele.get("value"): ele for ele in data}
def _get_all_system_config():
data = {}
from dvadmin.system.models import SystemConfig
system_config_obj = (
SystemConfig.objects.filter(parent_id__isnull=False)
.values("parent__key", "key", "value", "form_item_type")
.order_by("sort")
)
for system_config in system_config_obj:
value = system_config.get("value", "")
if value and system_config.get("form_item_type") == 7:
value = value[0].get("url")
if value and system_config.get("form_item_type") == 11:
new_value = []
for ele in value:
new_value.append({
"key": ele.get('key'),
"title": ele.get('title'),
"value": ele.get('value'),
})
new_value.sort(key=lambda s: s["key"])
value = new_value
data[f"{system_config.get('parent__key')}.{system_config.get('key')}"] = value
return data
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
for tenant in get_tenant_model().objects.filter():
with tenant_context(tenant):
settings.DICTIONARY_CONFIG[connection.tenant.schema_name] = _get_all_dictionary()
else:
settings.DICTIONARY_CONFIG = _get_all_dictionary()
except Exception as e:
print("请先进行数据库迁移!")
return
def init_system_config():
"""
初始化系统配置
:param name:
: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
for tenant in get_tenant_model().objects.filter():
with tenant_context(tenant):
settings.SYSTEM_CONFIG[connection.tenant.schema_name] = _get_all_system_config()
else:
settings.SYSTEM_CONFIG = _get_all_system_config()
except Exception as e:
print("请先进行数据库迁移!")
return
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
for tenant in get_tenant_model().objects.filter():
with tenant_context(tenant):
settings.DICTIONARY_CONFIG[connection.tenant.schema_name] = _get_all_dictionary()
else:
settings.DICTIONARY_CONFIG = _get_all_dictionary()
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
for tenant in get_tenant_model().objects.filter():
with tenant_context(tenant):
settings.SYSTEM_CONFIG[connection.tenant.schema_name] = _get_all_system_config()
else:
settings.SYSTEM_CONFIG = _get_all_system_config()
# ================================================= #
# ******************** 字典管理 ******************** #
# ================================================= #
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():
dictionary_config = settings.DICTIONARY_CONFIG[schema_name or connection.tenant.schema_name]
else:
dictionary_config = settings.DICTIONARY_CONFIG
return dictionary_config or {}
def get_dictionary_values(key, schema_name=None):
"""
获取字典数据数组
:param key: 对应字典配置的key值(字典编号)
: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)
def get_dictionary_label(key, name, schema_name=None):
"""
获取获取字典label值
:param key: 字典管理中的key值(字典编号)
:param name: 对应字典配置的value值
:param schema_name: 对应字典配置的租户schema_name值
:return:
"""
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 ""
# ================================================= #
# ******************** 系统配置 ******************** #
# ================================================= #
def get_system_config(schema_name=None):
"""
获取系统配置中所有配置
1.只传父级的key返回全部子级{ "父级key.子级key" : "" }
2."父级key.子级key"返回子级值
: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():
dictionary_config = settings.SYSTEM_CONFIG[schema_name or connection.tenant.schema_name]
else:
dictionary_config = settings.SYSTEM_CONFIG
return dictionary_config or {}
def get_system_config_values(key, schema_name=None):
"""
获取系统配置数据数组
:param key: 对应系统配置的key值(字典编号)
: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值
:param key: 系统配置中的key值(字典编号)
:param name: 对应系统配置的value值
:param schema_name: 对应系统配置的租户schema_name值
:return:
"""
children = get_system_config_values(key, schema_name) or []
for ele in children:
if ele.get("value") == str(name):
return ele.get("label")
return ""

425
application/settings.py Normal file
View File

@ -0,0 +1,425 @@
"""
Django settings for application project.
Generated by 'django-admin startproject' using Django 3.2.3.
For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/
"""
import os
import sys
from pathlib import Path
from datetime import timedelta
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# ================================================= #
# ******************** 动态配置 ******************** #
# ================================================= #
from conf.env import *
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure--z8%exyzt7e_%i@1+#1mm=%lb5=^fx_57=1@a+_y7bg5-w%)sm"
# 初始化plugins插件路径到环境变量中
PLUGINS_PATH = os.path.join(BASE_DIR, "plugins")
sys.path.insert(0, os.path.join(PLUGINS_PATH))
[
sys.path.insert(0, os.path.join(PLUGINS_PATH, ele))
for ele in os.listdir(PLUGINS_PATH)
if os.path.isdir(os.path.join(PLUGINS_PATH, ele)) and not ele.startswith("__")
]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = locals().get("DEBUG", True)
ALLOWED_HOSTS = locals().get("ALLOWED_HOSTS", ["*"])
# 列权限需要排除的App应用
COLUMN_EXCLUDE_APPS = ['channels', 'captcha'] + locals().get("COLUMN_EXCLUDE_APPS", [])
INSTALLED_APPS = [
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_comment_migrate",
"rest_framework",
"django_filters",
"corsheaders", # 注册跨域app
"drf_yasg",
"captcha",
"channels",
"dvadmin.system",
# "django_tenants",
]
My_Apps = [
'crud_book', #新的应用写在这里
]
INSTALLED_APPS += My_Apps
MIDDLEWARE = [
"dvadmin.utils.middleware.HealthCheckMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", # 跨域中间件
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"dvadmin.utils.middleware.ApiLoggingMiddleware",
]
ROOT_URLCONF = "application.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "application.wsgi.application"
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": DATABASE_ENGINE,
"NAME": DATABASE_NAME,
"USER": DATABASE_USER,
"PASSWORD": DATABASE_PASSWORD,
"HOST": DATABASE_HOST,
"PORT": DATABASE_PORT,
}
}
AUTH_USER_MODEL = "system.Users"
USERNAME_FIELD = "username"
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = "zh-hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = "/static/"
# # 设置django的静态文件目录
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
MEDIA_ROOT = "media" # 项目下的目录
MEDIA_URL = "/media/" # 跟STATIC_URL类似指定用户可以通过这个url找到文件
#添加以下代码以后就不用写{% load staticfiles %},可以直接引用
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder"
)
# 收集静态文件,必须将 MEDIA_ROOT,STATICFILES_DIRS先注释
# python manage.py collectstatic
# STATIC_ROOT=os.path.join(BASE_DIR,'static')
# ================================================= #
# ******************* 跨域的配置 ******************* #
# ================================================= #
# 全部允许配置
CORS_ORIGIN_ALLOW_ALL = True
# 允许cookie
CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中后端是否支持对cookie的操作
# ===================================================== #
# ********************* channels配置 ******************* #
# ===================================================== #
ASGI_APPLICATION = 'application.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
# CHANNEL_LAYERS = {
# 'default': {
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
# 'CONFIG': {
# "hosts": [('127.0.0.1', 6379)], #需修改
# },
# },
# }
# ================================================= #
# ********************* 日志配置 ******************* #
# ================================================= #
# # log 配置部分BEGIN #
SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
LOGS_FILE = os.path.join(BASE_DIR, "logs")
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
os.makedirs(os.path.join(BASE_DIR, "logs"))
# 格式:[2020-04-22 23:33:01][micoservice.apps.ready():16] [INFO] 这是一条日志:
# 格式:[日期][模块.函数名称():行号] [级别] 信息
STANDARD_LOG_FORMAT = (
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
)
CONSOLE_LOG_FORMAT = (
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
)
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {"format": STANDARD_LOG_FORMAT},
"console": {
"format": CONSOLE_LOG_FORMAT,
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"file": {
"format": CONSOLE_LOG_FORMAT,
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"handlers": {
"file": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": SERVER_LOGS_FILE,
"maxBytes": 1024 * 1024 * 100, # 100 MB
"backupCount": 5, # 最多备份5个
"formatter": "standard",
"encoding": "utf-8",
},
"error": {
"level": "ERROR",
"class": "logging.handlers.RotatingFileHandler",
"filename": ERROR_LOGS_FILE,
"maxBytes": 1024 * 1024 * 100, # 100 MB
"backupCount": 3, # 最多备份3个
"formatter": "standard",
"encoding": "utf-8",
},
"console": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "console",
},
},
"loggers": {
"": {
"handlers": ["console", "error", "file"],
"level": "INFO",
},
"django": {
"handlers": ["console", "error", "file"],
"level": "INFO",
"propagate": False,
},
'django.db.backends': {
'handlers': ["console", "error", "file"],
'propagate': False,
'level': "INFO"
},
"uvicorn.error": {
"level": "INFO",
"handlers": ["console", "error", "file"],
},
"uvicorn.access": {
"handlers": ["console", "error", "file"],
"level": "INFO"
},
},
}
# ================================================= #
# *************** REST_FRAMEWORK配置 *************** #
# ================================================= #
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.MultiPartParser',
),
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
"DATE_FORMAT": "%Y-%m-%d",
"DEFAULT_FILTER_BACKENDS": (
# 'django_filters.rest_framework.DjangoFilterBackend',
"dvadmin.utils.filters.CustomDjangoFilterBackend",
"rest_framework.filters.SearchFilter",
"rest_framework.filters.OrderingFilter",
),
"DEFAULT_PAGINATION_CLASS": "dvadmin.utils.pagination.CustomPagination", # 自定义分页
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated", # 只有经过身份认证确定用户身份才能访问
],
"EXCEPTION_HANDLER": "dvadmin.utils.exception.CustomExceptionHandler", # 自定义的异常处理
}
# ================================================= #
# ******************** 登录方式配置 ******************** #
# ================================================= #
AUTHENTICATION_BACKENDS = ["dvadmin.utils.backends.CustomBackend"]
# ================================================= #
# ****************** simplejwt配置 ***************** #
# ================================================= #
SIMPLE_JWT = {
# token有效时长
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=1440),
# token刷新后的有效时间
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
# 设置前缀
"AUTH_HEADER_TYPES": ("JWT",),
"ROTATE_REFRESH_TOKENS": True,
}
# ====================================#
# ****************swagger************#
# ====================================#
SWAGGER_SETTINGS = {
# 基础样式
"SECURITY_DEFINITIONS": {"basic": {"type": "basic"}},
# 如果需要登录才能够查看接口文档, 登录的链接使用restframework自带的.
"LOGIN_URL": "apiLogin/",
# 'LOGIN_URL': 'rest_framework:login',
"LOGOUT_URL": "rest_framework:logout",
# 'DOC_EXPANSION': None,
# 'SHOW_REQUEST_HEADERS':True,
# 'USE_SESSION_AUTH': True,
# 'DOC_EXPANSION': 'list',
# 接口文档中方法列表以首字母升序排列
"APIS_SORTER": "alpha",
# 如果支持json提交, 则接口文档中包含json输入框
"JSON_EDITOR": True,
# 方法列表字母排序
"OPERATIONS_SORTER": "alpha",
"VALIDATOR_URL": None,
"AUTO_SCHEMA_TYPE": 2, # 分组根据url层级分0、1 或 2 层
"DEFAULT_AUTO_SCHEMA_CLASS": "dvadmin.utils.swagger.CustomSwaggerAutoSchema",
}
# ================================================= #
# **************** 验证码配置 ******************* #
# ================================================= #
CAPTCHA_IMAGE_SIZE = (160, 46) # 设置 captcha 图片大小
CAPTCHA_LENGTH = 4 # 字符个数
CAPTCHA_TIMEOUT = 1 # 超时(minutes)
CAPTCHA_OUTPUT_FORMAT = "%(image)s %(text_field)s %(hidden_field)s "
CAPTCHA_FONT_SIZE = 36 # 字体大小
CAPTCHA_FOREGROUND_COLOR = "#64DAAA" # 前景色
CAPTCHA_BACKGROUND_COLOR = "#F5F7F4" # 背景色
CAPTCHA_NOISE_FUNCTIONS = (
"captcha.helpers.noise_arcs", # 线
# "captcha.helpers.noise_dots", # 点
)
# CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' #字母验证码
CAPTCHA_CHALLENGE_FUNCT = "captcha.helpers.math_challenge" # 加减乘除验证码
# ================================================= #
# ******************** 其他配置 ******************** #
# ================================================= #
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
API_LOG_ENABLE = True
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
API_LOG_METHODS = ["POST", "UPDATE", "DELETE", "PUT"] # ['POST', 'DELETE']
API_MODEL_MAP = {
"/token/": "登录模块",
"/api/login/": "登录模块",
"/api/plugins_market/plugins/": "插件市场",
}
DJANGO_CELERY_BEAT_TZ_AWARE = False
CELERY_TIMEZONE = "Asia/Shanghai" # celery 时区问题
# 静态页面压缩
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
ALL_MODELS_OBJECTS = [] # 所有app models 对象
# 初始化需要执行的列表,用来初始化后执行
INITIALIZE_LIST = []
INITIALIZE_RESET_LIST = []
# 表前缀
TABLE_PREFIX = locals().get('TABLE_PREFIX', "")
# 系统配置
SYSTEM_CONFIG = {}
# 字典配置
DICTIONARY_CONFIG = {}
# ================================================= #
# ******************** 插件配置 ******************** #
# ================================================= #
# 租户共享app
TENANT_SHARED_APPS = []
# 普通租户独有app
TENANT_EXCLUSIVE_APPS = []
# 插件 urlpatterns
PLUGINS_URL_PATTERNS = []
# 所有模式有的
SHARED_APPS = []
# ********** 一键导入插件配置开始 **********
# 例如:
# from dvadmin_upgrade_center.settings import * # 升级中心
from dvadmin3_celery.settings import * # celery 异步任务
# from dvadmin_third.settings import * # 第三方用户管理
# from dvadmin_ak_sk.settings import * # 秘钥管理管理
# from dvadmin_tenants.settings import * # 租户管理
#from dvadmin_social_auth.settings import *
#from dvadmin_uniapp.settings import *
# ...
# ********** 一键导入插件配置结束 **********

33
application/sse_views.py Normal file
View File

@ -0,0 +1,33 @@
# views.py
import time
import jwt
from django.http import StreamingHttpResponse
from application import settings
from dvadmin.system.models import MessageCenterTargetUser
from django.core.cache import cache
def event_stream(user_id):
last_sent_time = 0
while True:
# 从 Redis 中获取最后数据库变更时间
last_db_change_time = cache.get('last_db_change_time', 0)
# 只有当数据库发生变化时才检查总数
if last_db_change_time and last_db_change_time > last_sent_time:
count = MessageCenterTargetUser.objects.filter(users=user_id, is_read=False).count()
yield f"data: {count}\n\n"
last_sent_time = time.time()
time.sleep(1)
def sse_view(request):
token = request.GET.get('token')
decoded = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
user_id = decoded.get('user_id')
response = StreamingHttpResponse(event_stream(user_id), content_type='text/event-stream')
response['Cache-Control'] = 'no-cache'
return response

135
application/urls.py Normal file
View File

@ -0,0 +1,135 @@
"""backend URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf.urls.static import static
from django.urls import path, include, re_path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions
from rest_framework_simplejwt.views import (
TokenRefreshView,
)
from application import dispatch
from application import settings
from application.sse_views import sse_view
from dvadmin.system.views.dictionary import InitDictionaryViewSet
from dvadmin.system.views.login import (
LoginView,
CaptchaView,
ApiLogin,
LogoutView,
LoginTokenView
)
from dvadmin.system.views.system_config import InitSettingsViewSet
from dvadmin.utils.swagger import CustomOpenAPISchemaGenerator
# =========== 初始化系统配置 =================
dispatch.init_system_config()
dispatch.init_dictionary()
# =========== 初始化系统配置 =================
permission_classes = [permissions.AllowAny, ] if settings.DEBUG else [permissions.IsAuthenticated, ]
schema_view = get_schema_view(
openapi.Info(
title="Snippets API",
default_version="v1",
description="Test description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact@snippets.local"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=permission_classes,
generator_class=CustomOpenAPISchemaGenerator,
)
# 前端页面映射
from django.http import Http404, HttpResponse
from django.shortcuts import render
import mimetypes
import os
def web_view(request):
return render(request, 'web/index.html')
def serve_web_files(request, filename):
# 设定文件路径
filepath = os.path.join(settings.BASE_DIR, 'templates', 'web', filename)
# 检查文件是否存在
if not os.path.exists(filepath):
raise Http404("File does not exist")
# 根据文件扩展名,确定 MIME 类型
mime_type, _ = mimetypes.guess_type(filepath)
# 打开文件并读取内容
with open(filepath, 'rb') as f:
response = HttpResponse(f.read(), content_type=mime_type)
return response
urlpatterns = (
[
re_path(
r"^swagger(?P<format>\.json|\.yaml)$",
schema_view.without_ui(cache_timeout=0),
name="schema-json",
),
path(
"",
schema_view.with_ui("swagger", cache_timeout=0),
name="schema-swagger-ui",
),
path(
r"redoc/",
schema_view.with_ui("redoc", cache_timeout=0),
name="schema-redoc",
),
path("api/system/", include("dvadmin.system.urls")),
path("api/login/", LoginView.as_view(), name="token_obtain_pair"),
path("api/logout/", LogoutView.as_view(), name="token_obtain_pair"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
re_path(
r"^api-auth/", include("rest_framework.urls", namespace="rest_framework")
),
path("api/captcha/", CaptchaView.as_view()),
path("api/init/dictionary/", InitDictionaryViewSet.as_view()),
path("api/init/settings/", InitSettingsViewSet.as_view()),
path("apiLogin/", ApiLogin.as_view()),
# 仅用于开发,上线需关闭
path("api/token/", LoginTokenView.as_view()),
# 前端页面映射
path('web/', web_view, name='web_view'),
path('web/<path:filename>', serve_web_files, name='serve_web_files'),
# sse
path('sse/', sse_view, name='sse'),
]
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+ static(settings.STATIC_URL, document_root=settings.STATIC_URL)
+ [re_path(ele.get('re_path'), include(ele.get('include'))) for ele in settings.PLUGINS_URL_PATTERNS]
)
#就是添加如下内容把自己的路由单独写出来这样方便与dvadmin3的官方路由作区分
My_Urls = (
[ #这里的crud_book是指django创建的应用名称crud_book
path('',include('crud_book.urls')),]
)
# 这里把自己的路径单独出来,后面再追加在一起
urlpatterns += My_Urls

17
application/wsgi.py Normal file
View File

@ -0,0 +1,17 @@
"""
WSGI config for backend project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
application = get_wsgi_application()

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

50
conf/env.example.py Normal file
View File

@ -0,0 +1,50 @@
import os
from application.settings import BASE_DIR
# ================================================= #
# *************** mysql数据库 配置 *************** #
# ================================================= #
# 数据库 ENGINE ,默认演示使用 sqlite3 数据库,正式环境建议使用 mysql 数据库
# sqlite3 设置
# DATABASE_ENGINE = "django.db.backends.sqlite3"
# DATABASE_NAME = os.path.join(BASE_DIR, "db.sqlite3")
# 使用mysql时改为此配置
DATABASE_ENGINE = "django.db.backends.mysql"
DATABASE_NAME = 'django-vue3-admin' # mysql 时使用
# 数据库地址 改为自己数据库地址
DATABASE_HOST = '127.0.0.1'
# # 数据库端口
DATABASE_PORT = 3306
# # 数据库用户名
DATABASE_USER = "root"
# # 数据库密码
DATABASE_PASSWORD = 'DVADMIN3'
# 表前缀
TABLE_PREFIX = "dvadmin_"
# ================================================= #
# ******** redis配置无redis 可不进行配置 ******** #
# ================================================= #
REDIS_DB = 1
CELERY_BROKER_DB = 3
REDIS_PASSWORD = 'DVADMIN3'
REDIS_HOST = '127.0.0.1'
REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6379'
# ================================================= #
# ****************** 功能 启停 ******************* #
# ================================================= #
DEBUG = True
# 启动登录详细概略获取(通过调用api获取ip详细地址。如果是内网关闭即可)
ENABLE_LOGIN_ANALYSIS_LOG = True
# 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
LOGIN_NO_CAPTCHA_AUTH = True
# ================================================= #
# ****************** 其他 配置 ******************* #
# ================================================= #
ALLOWED_HOSTS = ["*"]
# 列权限中排除App应用
COLUMN_EXCLUDE_APPS = []

50
conf/env.py Normal file
View File

@ -0,0 +1,50 @@
import os
from application.settings import BASE_DIR
# ================================================= #
# *************** mysql数据库 配置 *************** #
# ================================================= #
# 数据库 ENGINE ,默认演示使用 sqlite3 数据库,正式环境建议使用 mysql 数据库
# sqlite3 设置
DATABASE_ENGINE = "django.db.backends.sqlite3"
DATABASE_NAME = os.path.join(BASE_DIR, "db.sqlite3")
# 使用mysql时改为此配置
#DATABASE_ENGINE = "django.db.backends.mysql"
#DATABASE_NAME = 'django-vue3-admin' # mysql 时使用
# 数据库地址 改为自己数据库地址
DATABASE_HOST = '127.0.0.1'
# # 数据库端口
DATABASE_PORT = 3306
# # 数据库用户名
DATABASE_USER = "root"
# # 数据库密码
DATABASE_PASSWORD = 'DVADMIN3'
# 表前缀
TABLE_PREFIX = "dvadmin_"
# ================================================= #
# ******** redis配置无redis 可不进行配置 ******** #
# ================================================= #
REDIS_DB = 1
CELERY_BROKER_DB = 3
REDIS_PASSWORD = 'DVADMIN3'
REDIS_HOST = '127.0.0.1'
REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6379'
# ================================================= #
# ****************** 功能 启停 ******************* #
# ================================================= #
DEBUG = True
# 启动登录详细概略获取(通过调用api获取ip详细地址。如果是内网关闭即可)
ENABLE_LOGIN_ANALYSIS_LOG = True
# 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
LOGIN_NO_CAPTCHA_AUTH = True
# ================================================= #
# ****************** 其他 配置 ******************* #
# ================================================= #
ALLOWED_HOSTS = ["*"]
# 列权限中排除App应用
COLUMN_EXCLUDE_APPS = []

0
crud_book/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
crud_book/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
crud_book/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class CrudBookConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'crud_book'

View File

@ -0,0 +1,52 @@
# Generated by Django 4.2.14 on 2025-10-17 00:21
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='CrudBookModel',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('title', models.CharField(max_length=255, verbose_name='书名')),
('sub_title', models.CharField(max_length=255, verbose_name='副标题')),
('series', models.CharField(max_length=255, verbose_name='系列丛书')),
('author', models.CharField(max_length=255, verbose_name='作者')),
('translator', models.CharField(max_length=255, verbose_name='译者')),
('subject', models.CharField(max_length=255, verbose_name='学科')),
('publisher', models.CharField(max_length=255, verbose_name='出版社')),
('abstract', models.CharField(max_length=255, verbose_name='内容摘要')),
('language', models.CharField(max_length=255, verbose_name='语种')),
('edition', models.IntegerField(verbose_name='版次')),
('isbn', models.IntegerField(verbose_name='ISBN')),
('create_time', models.DateField(verbose_name='创建时间')),
('create_by', models.DateField(verbose_name='创建者编号')),
('update_time', models.DateField(verbose_name='更新时间')),
('update_by', models.IntegerField(verbose_name='更新用户编号')),
('image', models.ImageField(upload_to='', verbose_name='封面')),
('location', models.CharField(max_length=255, verbose_name='存放位置')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '图书表',
'verbose_name_plural': '图书表',
'db_table': 'books',
'ordering': ('-create_time',),
},
),
]

View File

@ -0,0 +1,63 @@
# Generated by Django 4.2.14 on 2025-10-19 23:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('crud_book', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='crudbookmodel',
name='abstract',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='内容摘要'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='create_by',
field=models.IntegerField(default=1, verbose_name='创建者编号'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='edition',
field=models.IntegerField(blank=True, null=True, verbose_name='版次'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='', verbose_name='封面'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='isbn',
field=models.IntegerField(blank=True, null=True, verbose_name='ISBN'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='location',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='存放位置'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='series',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='系列丛书'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='translator',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='译者'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='update_by',
field=models.IntegerField(blank=True, null=True, verbose_name='更新用户编号'),
),
migrations.AlterField(
model_name='crudbookmodel',
name='update_time',
field=models.DateField(blank=True, null=True, verbose_name='更新时间'),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 4.2.14 on 2025-10-20 20:24
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('crud_book', '0002_alter_crudbookmodel_abstract_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='crudbookmodel',
options={'ordering': ('-create_datetime',), 'verbose_name': '图书表', 'verbose_name_plural': '图书表'},
),
migrations.RemoveField(
model_name='crudbookmodel',
name='create_by',
),
migrations.RemoveField(
model_name='crudbookmodel',
name='create_time',
),
migrations.RemoveField(
model_name='crudbookmodel',
name='update_by',
),
migrations.RemoveField(
model_name='crudbookmodel',
name='update_time',
),
]

View File

30
crud_book/models.py Normal file
View File

@ -0,0 +1,30 @@
from django.db import models
# Create your models here.
from dvadmin.utils.models import CoreModel
class CrudBookModel(CoreModel):
title = models.CharField(max_length=255, verbose_name="书名")
sub_title = models.CharField(max_length=255, verbose_name="副标题")
series = models.CharField(max_length=255, verbose_name="系列丛书", null=True, blank=True)
author = models.CharField(max_length=255, verbose_name="作者")
translator = models.CharField(max_length=255, verbose_name="译者", null=True, blank=True)
subject = models.CharField(max_length=255, verbose_name="学科")
publisher = models.CharField(max_length=255, verbose_name="出版社")
abstract = models.CharField(max_length=255, verbose_name="内容摘要", null=True, blank=True)
language = models.CharField(max_length=255, verbose_name="语种")
edition = models.IntegerField(verbose_name="版次", null=True, blank=True)
isbn = models.IntegerField(verbose_name="ISBN", null=True, blank=True)
# create_time = models.DateField(verbose_name="创建时间")
# create_by = models.IntegerField(verbose_name="创建者编号", default=1)
# update_time = models.DateField(verbose_name="更新时间", null=True, blank=True)
# update_by = models.IntegerField(verbose_name="更新用户编号", null=True, blank=True)
image = models.ImageField(verbose_name="封面", null=True, blank=True)
location = models.CharField(max_length=255, verbose_name="存放位置", null=True, blank=True)
class Meta:
db_table = "books"
verbose_name = '图书表'
verbose_name_plural = verbose_name
ordering = ('-create_datetime',)

24
crud_book/serializers.py Normal file
View File

@ -0,0 +1,24 @@
#backend/crud_demo/serializers.py
from crud_book.models import CrudBookModel
from dvadmin.utils.serializers import CustomModelSerializer
class CrudBookModelSerializer(CustomModelSerializer):
"""
序列化器
"""
#这里是进行了序列化模型及所有的字段
class Meta:
model = CrudBookModel
fields = "__all__"
#这里是创建/更新时的列化器
class CrudBookModelCreateUpdateSerializer(CustomModelSerializer):
"""
创建/更新时的列化器
"""
class Meta:
model = CrudBookModel
fields = '__all__'

3
crud_book/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
crud_book/urls.py Normal file
View File

@ -0,0 +1,13 @@
#backend/crud_demo/urls.py
from rest_framework.routers import SimpleRouter
from .views import CrudBookModelViewSet
router = SimpleRouter()
# 这里进行注册路径并把视图关联上这里的api地址以视图名称为后缀这样方便记忆api/CrudBookModelViewSet
router.register("api/CrudBookModelViewSet", CrudBookModelViewSet)
urlpatterns = [
]
urlpatterns += router.urls

18
crud_book/views.py Normal file
View File

@ -0,0 +1,18 @@
# Create your views here.
from crud_book.models import CrudBookModel
from crud_book.serializers import CrudBookModelSerializer, CrudBookModelCreateUpdateSerializer
from dvadmin.utils.viewset import CustomModelViewSet
class CrudBookModelViewSet(CustomModelViewSet):
"""
list:查询
create:新增
update:修改
retrieve:单例
destroy:删除
"""
queryset = CrudBookModel.objects.all()
serializer_class = CrudBookModelSerializer
create_serializer_class = CrudBookModelCreateUpdateSerializer
update_serializer_class = CrudBookModelCreateUpdateSerializer

BIN
db.sqlite3 Normal file

Binary file not shown.

15
del_migrations.py Normal file
View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
import os
exclude = ["venv", ".venv"] # 需要排除的文件目录
for root, dirs, files in os.walk('.'):
dirs[:] = list(set(dirs) - set(exclude))
if 'migrations' in dirs:
dir = dirs[dirs.index('migrations')]
for root_j, dirs_j, files_j in os.walk(os.path.join(root, dir)):
for file_k in files_j:
if file_k != '__init__.py':
dst_file = os.path.join(root_j, file_k)
print('删除文件>>> ', dst_file)
os.remove(dst_file)

5
docker_start.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
# python manage.py makemigrations
# python manage.py migrate
# python manage.py init -y
uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 4

1
dvadmin/__init__.py Normal file
View File

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
dvadmin/system/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

10
dvadmin/system/apps.py Normal file
View File

@ -0,0 +1,10 @@
from django.apps import AppConfig
class SystemConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'dvadmin.system'
def ready(self):
# 注册信号
import dvadmin.system.signals # 确保路径正确

View File

View File

@ -0,0 +1,415 @@
# -*- coding: utf-8 -*-
import os
from rest_framework import serializers
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
import django
django.setup()
from dvadmin.system.models import (
Role, Dept, Users, Menu, MenuButton,
ApiWhiteList, Dictionary, SystemConfig,
RoleMenuPermission, RoleMenuButtonPermission, MenuField
)
from dvadmin.utils.serializers import CustomModelSerializer
class UsersInitSerializer(CustomModelSerializer):
"""
初始化获取数信息(用于生成初始化json文件)
"""
role_key = serializers.SerializerMethodField()
dept_key = serializers.SerializerMethodField()
def get_dept_key(self, obj):
if obj.dept:
return obj.dept.key
else:
return None
def get_role_key(self, obj):
if obj.role.all():
return [role.key for role in obj.role.all()]
else:
return []
def save(self, **kwargs):
instance = super().save(**kwargs)
role_key = self.initial_data.get('role_key', [])
role_ids = Role.objects.filter(key__in=role_key).values_list('id', flat=True)
instance.role.set(role_ids)
dept_key = self.initial_data.get('dept_key', None)
dept_id = Dept.objects.filter(key=dept_key).first()
instance.dept = dept_id
instance.save()
return instance
class Meta:
model = Users
fields = ["username", "email", 'mobile', 'avatar', "name", 'gender', 'user_type', "dept", 'user_type',
'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'creator', 'dept_belong_id',
'password', 'last_login', 'is_superuser', 'role_key' ,'dept_key']
read_only_fields = ['id']
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
class MenuButtonInitSerializer(CustomModelSerializer):
"""
初始化菜单按钮-序列化器
"""
class Meta:
model = MenuButton
fields = ['id', 'name', 'value', 'api', 'method', 'menu']
read_only_fields = ["id"]
class MenuFieldInitSerializer(CustomModelSerializer):
"""
初始化列权限-序列化器
"""
class Meta:
model = MenuField
fields = ['id', 'menu', 'field_name', 'title', 'model']
read_only_fields = ["id"]
class MenuInitSerializer(CustomModelSerializer):
"""
递归深度获取数信息(用于生成初始化json文件)
"""
name = serializers.CharField(required=False)
children = serializers.SerializerMethodField()
menu_button = serializers.SerializerMethodField()
menu_field = serializers.SerializerMethodField()
def get_children(self, obj: Menu):
data = []
instance = Menu.objects.filter(parent_id=obj.id)
if instance:
serializer = MenuInitSerializer(instance=instance, many=True)
data = serializer.data
return data
def get_menu_button(self, obj: Menu):
data = []
instance = obj.menuPermission.order_by('method')
if instance:
data = list(instance.values('name', 'value', 'api', 'method'))
return data
def get_menu_field(self, obj: Menu):
data = []
instance = obj.menufield_set.order_by('field_name')
if instance:
data = list(instance.values('field_name', 'title', 'model'))
return data
def save(self, **kwargs):
instance = super().save(**kwargs)
children = self.initial_data.get('children')
menu_button = self.initial_data.get('menu_button')
menu_field = self.initial_data.get('menu_field')
# 菜单表
if children:
for menu_data in children:
menu_data['parent'] = instance.id
filter_data = {
"name": menu_data['name'],
"web_path": menu_data['web_path'],
"component": menu_data['component'],
"component_name": menu_data['component_name'],
}
instance_obj = Menu.objects.filter(**filter_data).first()
if instance_obj and not self.initial_data.get('reset'):
continue
serializer = MenuInitSerializer(instance_obj, data=menu_data, request=self.request)
serializer.is_valid(raise_exception=True)
serializer.save()
# 菜单按钮
if menu_button:
for menu_button_data in menu_button:
menu_button_data['menu'] = instance.id
filter_data = {
"menu": menu_button_data['menu'],
"value": menu_button_data['value']
}
instance_obj = MenuButton.objects.filter(**filter_data).first()
serializer = MenuButtonInitSerializer(instance_obj, data=menu_button_data, request=self.request)
serializer.is_valid(raise_exception=True)
serializer.save()
# 列权限
if menu_field:
for field_data in menu_field:
field_data['menu'] = instance.id
filter_data = {
'menu': field_data['menu'],
'field_name': field_data['field_name'],
'model': field_data['model']
}
instance_obj = MenuField.objects.filter(**filter_data).first()
serializer = MenuFieldInitSerializer(instance_obj, data=field_data, request=self.request)
serializer.is_valid(raise_exception=True)
serializer.save()
return instance
class Meta:
model = Menu
fields = ['name', 'icon', 'sort', 'is_link', 'is_catalog', 'web_path', 'component', 'component_name', 'status',
'cache', 'visible', 'parent', 'children', 'menu_button', 'menu_field', 'creator', 'dept_belong_id']
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
read_only_fields = ['id', 'children']
class RoleInitSerializer(CustomModelSerializer):
"""
初始化获取数信息(用于生成初始化json文件)
"""
class Meta:
model = Role
fields = ['name', 'key', 'sort', 'status',
'creator', 'dept_belong_id']
read_only_fields = ["id"]
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
class RoleMenuInitSerializer(CustomModelSerializer):
"""
初始化角色菜单(用于生成初始化json文件)
"""
role__key = serializers.CharField(source='role.key')
menu__web_path = serializers.CharField(source='menu.web_path')
menu__component_name = serializers.CharField(source='menu.component_name', allow_blank=True)
def update(self, instance, validated_data):
init_data = self.initial_data
role_id = Role.objects.filter(key=init_data['role__key']).first()
menu_id = Menu.objects.filter(web_path=init_data['menu__web_path'], component_name=init_data['menu__component_name']).first()
validated_data['role'] = role_id
validated_data['menu'] = menu_id
return super().update(instance, validated_data)
def create(self, validated_data):
init_data = self.initial_data
role_id = Role.objects.filter(key=init_data['role__key']).first()
menu_id = Menu.objects.filter(web_path=init_data['menu__web_path'], component_name=init_data['menu__component_name']).first()
validated_data['role'] = role_id
validated_data['menu'] = menu_id
return super().create(validated_data)
class Meta:
model = RoleMenuPermission
fields = ['role__key', 'menu__web_path', 'menu__component_name','creator', 'dept_belong_id']
read_only_fields = ["id"]
extra_kwargs = {
'role': {'required': False},
'menu': {'required': False},
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
class RoleMenuButtonInitSerializer(CustomModelSerializer):
"""
初始化角色菜单按钮(用于生成初始化json文件)
"""
role__key = serializers.CharField(source='role.key')
menu_button__value = serializers.CharField(source='menu_button.value')
data_range = serializers.CharField(max_length=100, required=False)
def update(self, instance, validated_data):
init_data = self.initial_data
role_id = Role.objects.filter(key=init_data['role__key']).first()
menu_button_id = MenuButton.objects.filter(value=init_data['menu_button__value']).first()
validated_data['role'] = role_id
validated_data['menu_button'] = menu_button_id
instance = super().create(validated_data)
instance.dept.set([])
return super().update(instance, validated_data)
def create(self, validated_data):
init_data = self.initial_data
role_id = Role.objects.filter(key=init_data['role__key']).first()
menu_button_id = MenuButton.objects.filter(value=init_data['menu_button__value']).first()
validated_data['role'] = role_id
validated_data['menu_button'] = menu_button_id
instance = super().create(validated_data)
instance.dept.set([])
return instance
def save(self, **kwargs):
if not self.instance or self.initial_data.get('reset'):
return super().save(**kwargs)
return self.instance
class Meta:
model = RoleMenuButtonPermission
fields = ['role__key', 'menu_button__value', 'data_range', 'dept', 'creator', 'dept_belong_id']
read_only_fields = ["id"]
extra_kwargs = {
'role': {'required': False},
'menu': {'required': False},
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
class ApiWhiteListInitSerializer(CustomModelSerializer):
"""
初始化获取数信息(用于生成初始化json文件)
"""
class Meta:
model = ApiWhiteList
fields = ['url', 'method', 'enable_datasource', 'creator', 'dept_belong_id']
read_only_fields = ["id"]
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
class DeptInitSerializer(CustomModelSerializer):
"""
递归深度获取数信息(用于生成初始化json文件)
"""
children = serializers.SerializerMethodField()
def get_children(self, obj: Dept):
data = []
instance = Dept.objects.filter(parent_id=obj.id)
if instance:
serializer = DeptInitSerializer(instance=instance, many=True)
data = serializer.data
return data
def save(self, **kwargs):
instance = super().save(**kwargs)
children = self.initial_data.get('children')
if children:
for menu_data in children:
menu_data['parent'] = instance.id
filter_data = {
"name": menu_data['name'],
"parent": menu_data['parent'],
"key": menu_data['key']
}
instance_obj = Dept.objects.filter(**filter_data).first()
if instance_obj and not self.initial_data.get('reset'):
continue
serializer = DeptInitSerializer(instance_obj, data=menu_data, request=self.request)
serializer.is_valid(raise_exception=True)
serializer.save()
return instance
class Meta:
model = Dept
fields = ['name', 'sort', 'owner', 'phone', 'email', 'status', 'parent', 'creator', 'dept_belong_id',
'children', 'key']
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
read_only_fields = ['id', 'children']
class DictionaryInitSerializer(CustomModelSerializer):
"""
初始化获取数信息(用于生成初始化json文件)
"""
children = serializers.SerializerMethodField()
def get_children(self, obj: Dictionary):
data = []
instance = Dictionary.objects.filter(parent_id=obj.id)
if instance:
serializer = DictionaryInitSerializer(instance=instance, many=True)
data = serializer.data
return data
def save(self, **kwargs):
instance = super().save(**kwargs)
children = self.initial_data.get('children')
# 菜单表
if children:
for data in children:
data['parent'] = instance.id
filter_data = {
"value": data['value'],
"parent": data['parent']
}
instance_obj = Dictionary.objects.filter(**filter_data).first()
if instance_obj and not self.initial_data.get('reset'):
continue
serializer = DictionaryInitSerializer(instance_obj, data=data, request=self.request)
serializer.is_valid(raise_exception=True)
serializer.save()
return instance
class Meta:
model = Dictionary
fields = ['label', 'value', 'parent', 'type', 'color', 'is_value', 'status', 'sort', 'remark', 'creator',
'dept_belong_id', 'children']
read_only_fields = ["id"]
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}
class SystemConfigInitSerializer(CustomModelSerializer):
"""
初始化获取数信息(用于生成初始化json文件)
"""
children = serializers.SerializerMethodField()
def get_children(self, obj: SystemConfig):
data = []
instance = SystemConfig.objects.filter(parent_id=obj.id)
if instance:
serializer = SystemConfigInitSerializer(instance=instance, many=True)
data = serializer.data
return data
def save(self, **kwargs):
instance = super().save(**kwargs)
children = self.initial_data.get('children')
# 菜单表
if children:
for data in children:
data['parent'] = instance.id
filter_data = {
"key": data['key'],
"parent": data['parent']
}
instance_obj = SystemConfig.objects.filter(**filter_data).first()
if instance_obj and not self.initial_data.get('reset'):
continue
serializer = SystemConfigInitSerializer(instance_obj, data=data, request=self.request)
serializer.is_valid(raise_exception=True)
serializer.save()
return instance
class Meta:
model = SystemConfig
fields = ['parent', 'title', 'key', 'value', 'sort', 'status', 'data_options', 'form_item_type', 'rule',
'placeholder', 'setting', 'creator', 'dept_belong_id', 'children']
read_only_fields = ["id"]
extra_kwargs = {
'creator': {'write_only': True},
'dept_belong_id': {'write_only': True}
}

View File

@ -0,0 +1,7 @@
[
{
"url": "/api/system/dept_lazy_tree/",
"method": 0,
"enable_datasource": true
}
]

View File

@ -0,0 +1,36 @@
[
{
"name": "DVAdmin团队",
"key": "dvadmin",
"sort": 1,
"owner": "",
"phone": "",
"email": "",
"status": true,
"parent": null,
"children": [
{
"name": "运营部",
"key": "",
"sort": 2,
"owner": "",
"phone": "",
"email": "",
"status": true,
"parent": 1,
"children": []
},
{
"name": "技术部",
"key": "technology",
"sort": 1,
"owner": "",
"phone": "",
"email": "",
"status": true,
"parent": 3,
"children": []
}
]
}
]

View File

@ -0,0 +1,595 @@
[
{
"label": "启用/禁用-布尔值",
"value": "button_status_bool",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 1,
"remark": null,
"children": [
{
"label": "启用",
"value": "true",
"parent": 1,
"type": 6,
"color": "success",
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "禁用",
"value": "false",
"parent": 1,
"type": 6,
"color": "danger",
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
}
]
},
{
"label": "系统按钮",
"value": "system_button",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 2,
"remark": null,
"children": [
{
"label": "新增",
"value": "Create",
"parent": 4,
"type": 0,
"color": "success",
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "编辑",
"value": "Update",
"parent": 4,
"type": 0,
"color": "primary",
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
},
{
"label": "删除",
"value": "Delete",
"parent": 4,
"type": 0,
"color": "danger",
"is_value": true,
"status": true,
"sort": 3,
"remark": null,
"children": []
},
{
"label": "详情",
"value": "Retrieve",
"parent": 4,
"type": 0,
"color": "info",
"is_value": true,
"status": true,
"sort": 4,
"remark": null,
"children": []
},
{
"label": "查询",
"value": "Search",
"parent": 4,
"type": 0,
"color": "warning",
"is_value": true,
"status": true,
"sort": 5,
"remark": null,
"children": []
},
{
"label": "保存",
"value": "Save",
"parent": 4,
"type": 0,
"color": "success",
"is_value": true,
"status": true,
"sort": 6,
"remark": null,
"children": []
},
{
"label": "导入",
"value": "Import",
"parent": 4,
"type": 0,
"color": "primary",
"is_value": true,
"status": true,
"sort": 7,
"remark": null,
"children": []
},
{
"label": "导出",
"value": "Export",
"parent": 4,
"type": 0,
"color": "warning",
"is_value": true,
"status": true,
"sort": 8,
"remark": null,
"children": []
}
]
},
{
"label": "启用/禁用-数字值",
"value": "button_status_number",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 3,
"remark": null,
"children": [
{
"label": "启用",
"value": "1",
"parent": 13,
"type": 1,
"color": "success",
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "禁用",
"value": "0",
"parent": 13,
"type": 1,
"color": "danger",
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
}
]
},
{
"label": "是/否-布尔值",
"value": "button_whether_bool",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 4,
"remark": null,
"children": [
{
"label": "是",
"value": "true",
"parent": 16,
"type": 6,
"color": "success",
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "否",
"value": "false",
"parent": 16,
"type": 6,
"color": "danger",
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
}
]
},
{
"label": "是/否-数字值",
"value": "button_whether_number",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 5,
"remark": null,
"children": [
{
"label": "是",
"value": "1",
"parent": 19,
"type": 1,
"color": "success",
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "否",
"value": "2",
"parent": 19,
"type": 1,
"color": "danger",
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
}
]
},
{
"label": "用户类型",
"value": "user_type",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 6,
"remark": null,
"children": [
{
"label": "后台用户",
"value": "0",
"parent": 22,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "前台用户",
"value": "1",
"parent": 22,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
}
]
},
{
"label": "表单类型",
"value": "config_form_type",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 7,
"remark": null,
"children": [
{
"label": "text",
"value": "0",
"parent": 25,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 0,
"remark": null,
"children": []
},
{
"label": "textarea",
"value": "3",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 0,
"remark": null,
"children": []
},
{
"label": "number",
"value": "10",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 0,
"remark": null,
"children": []
},
{
"label": "datetime",
"value": "1",
"parent": 25,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "date",
"value": "2",
"parent": 25,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
},
{
"label": "time",
"value": "15",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 3,
"remark": null,
"children": []
},
{
"label": "select",
"value": "4",
"parent": 25,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 4,
"remark": null,
"children": []
},
{
"label": "checkbox",
"value": "5",
"parent": 25,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 5,
"remark": null,
"children": []
},
{
"label": "radio",
"value": "6",
"parent": 25,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 6,
"remark": null,
"children": []
},
{
"label": "switch",
"value": "9",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 6,
"remark": null,
"children": []
},
{
"label": "文件附件",
"value": "8",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 7,
"remark": null,
"children": []
},
{
"label": "图片(单张)",
"value": "7",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 8,
"remark": null,
"children": []
},
{
"label": "图片(多张)",
"value": "12",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 9,
"remark": null,
"children": []
},
{
"label": "数组",
"value": "11",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 11,
"remark": null,
"children": []
},
{
"label": "关联表",
"value": "13",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 13,
"remark": null,
"children": []
},
{
"label": "关联表(多选)",
"value": "14",
"parent": 25,
"type": 1,
"color": "",
"is_value": true,
"status": true,
"sort": 14,
"remark": null,
"children": []
}
]
},
{
"label": "性别",
"value": "gender",
"parent": null,
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 8,
"remark": null,
"children": [
{
"label": "未知",
"value": "0",
"parent": 42,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 0,
"remark": null,
"children": []
},
{
"label": "男",
"value": "1",
"parent": 42,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "女",
"value": "2",
"parent": 42,
"type": 1,
"color": null,
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
}
]
},
{
"label": "文件存储引擎",
"value": "file_engine",
"type": 0,
"color": null,
"is_value": false,
"status": true,
"sort": 9,
"remark": null,
"children": [
{
"label": "本地",
"value": "local",
"type": 0,
"color": "primary",
"is_value": true,
"status": true,
"sort": 1,
"remark": null,
"children": []
},
{
"label": "阿里云oss",
"value": "oss",
"type": 0,
"color": "success",
"is_value": true,
"status": true,
"sort": 2,
"remark": null,
"children": []
},
{
"label": "腾讯cos",
"value": "cos",
"type": 0,
"color": "warning",
"is_value": true,
"status": true,
"sort": 3,
"remark": null,
"children": []
}
]
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
[
{
"name": "管理员",
"key": "admin",
"sort": 1,
"status": true,
"remark": null
},
{
"name": "用户",
"key": "public",
"sort": 2,
"status": true,
"remark": null
}
]

View File

@ -0,0 +1,7 @@
[
{
"role__key": "admin",
"menu_button__value": "menu:Search",
"data_range": 0
}
]

View File

@ -0,0 +1,12 @@
[
{
"role__key": "admin",
"menu__web_path": "/system",
"menu__component_name": ""
},
{
"role__key": "admin",
"menu__web_path": "/menu",
"menu__component_name": "menu"
}
]

View File

@ -0,0 +1,486 @@
[
{
"parent": null,
"title": "基础配置",
"key": "base",
"value": null,
"sort": 0,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": null,
"placeholder": null,
"setting": null,
"children": [
{
"parent": 10,
"title": "网页标题",
"key": "web_title",
"value": "DVAdmin",
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [],
"placeholder": "请输入网站标题",
"setting": null,
"children": []
},
{
"parent": 10,
"title": "网站小图标",
"key": "web_favicon",
"value": "",
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [],
"placeholder": "请输入网站小图标",
"setting": null,
"children": []
},
{
"parent": 10,
"title": "开启验证码",
"key": "captcha_state",
"value": true,
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 9,
"rule": [
{
"message": "必填项不能为空",
"required": true
}
],
"placeholder": "请选择",
"setting": null,
"children": []
},
{
"parent": 10,
"title": "创建用户默认密码",
"key": "default_password",
"value": "admin123456",
"sort": 2,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"message": "必填项不能为空",
"required": true
}
],
"placeholder": "请输入默认密码",
"setting": null,
"children": []
}
]
},
{
"parent": null,
"title": "登录页配置",
"key": "login",
"value": null,
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": null,
"placeholder": null,
"setting": null,
"children": [
{
"parent": 1,
"title": "网站标题",
"key": "site_title",
"value": "Dvadmin",
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [],
"placeholder": "请输入网站标题",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "网站名称",
"key": "site_name",
"value": "企业级后台管理系统",
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"message": "必填项不能为空",
"required": true
}
],
"placeholder": "请输入网站名称",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "登录网站logo",
"key": "site_logo",
"value": null,
"sort": 2,
"status": true,
"data_options": null,
"form_item_type": 7,
"rule": [],
"placeholder": "请上传网站logo",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "登录页背景图",
"key": "login_background",
"value": null,
"sort": 3,
"status": true,
"data_options": null,
"form_item_type": 7,
"rule": [],
"placeholder": "请上传登录背景页",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "版权信息",
"key": "copyright",
"value": "2021-2024 django-vue-admin.com 版权所有",
"sort": 4,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"message": "必填项不能为空",
"required": true
}
],
"placeholder": "请输入版权信息",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "备案信息",
"key": "keep_record",
"value": "晋ICP备18005113号-3",
"sort": 5,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"message": "必填项不能为空",
"required": true
}
],
"placeholder": "请输入备案信息",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "帮助链接",
"key": "help_url",
"value": "https://django-vue-admin.com",
"sort": 6,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": "",
"placeholder": "请输入帮助信息",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "隐私链接",
"key": "privacy_url",
"value": "/api/system/clause/privacy.html",
"sort": 7,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [],
"placeholder": "请填写隐私链接",
"setting": null,
"children": []
},
{
"parent": 1,
"title": "条款链接",
"key": "clause_url",
"value": "/api/system/clause/terms_service.html",
"sort": 8,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": [],
"placeholder": "请输入条款链接",
"setting": null,
"children": []
}
]
},
{
"title": "文件存储配置",
"key": "file_storage",
"value": null,
"sort": 0,
"status": true,
"data_options": null,
"form_item_type": 0,
"rule": null,
"placeholder": null,
"setting": null,
"children": [
{
"title": "存储引擎",
"key": "file_engine",
"value": "local",
"sort": 1,
"status": true,
"data_options": null,
"form_item_type": 4,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请选择存储引擎",
"setting": "file_engine",
"children": []
},
{
"title": "文件是否备份",
"key": "file_backup",
"value": false,
"sort": 2,
"status": true,
"data_options": null,
"form_item_type": 9,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "启用云存储时,文件是否备份到本地",
"setting": null,
"children": []
},
{
"title": "阿里云-AccessKey",
"key": "aliyun_access_key",
"value": null,
"sort": 3,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入AccessKey",
"setting": null,
"children": []
},
{
"title": "阿里云-Secret",
"key": "aliyun_access_secret",
"value": null,
"sort": 4,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入Secret",
"setting": null,
"children": []
},
{
"title": "阿里云-Endpoint",
"key": "aliyun_endpoint",
"value": null,
"sort": 5,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入Endpoint",
"setting": null,
"children": []
},
{
"title": "阿里云-上传路径",
"key": "aliyun_path",
"value": "/media/",
"sort": 5,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入上传路径",
"setting": null,
"children": []
},
{
"title": "阿里云-Bucket",
"key": "aliyun_bucket",
"value": null,
"sort": 7,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入Bucket",
"setting": null,
"children": []
},{
"title": "阿里云-cdn地址",
"key": "aliyun_cdn_url",
"value": null,
"sort": 7,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入cdn地址",
"setting": null,
"children": []
},
{
"title": "腾讯云-SecretId",
"key": "tencent_secret_id",
"value": null,
"sort": 8,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入SecretId",
"setting": null,
"children": []
},
{
"title": "腾讯云-SecretKey",
"key": "tencent_secret_key",
"value": null,
"sort": 9,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入SecretKey",
"setting": null,
"children": []
},
{
"title": "腾讯云-Region",
"key": "tencent_region",
"value": null,
"sort": 10,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入Region",
"setting": null,
"children": []
},
{
"title": "腾讯云-Bucket",
"key": "tencent_bucket",
"value": null,
"sort": 11,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入Bucket",
"setting": null,
"children": []
},
{
"title": "腾讯云-上传路径",
"key": "tencent_path",
"value": "/media/",
"sort": 12,
"status": false,
"data_options": null,
"form_item_type": 0,
"rule": [
{
"required": false,
"message": "必填项不能为空"
}
],
"placeholder": "请输入上传路径",
"setting": null,
"children": []
}
]
}
]

View File

@ -0,0 +1,60 @@
[
{
"username": "superadmin",
"email": "dvadmin@django-vue-admin.com",
"mobile": "13333333333",
"avatar": null,
"name": "超级管理员",
"gender": 1,
"user_type": 0,
"role": [],
"role_key": [
"admin"
],
"dept_key": "dvadmin",
"first_name": "",
"last_name": "",
"is_staff": true,
"is_active": true,
"password": "pbkdf2_sha256$260000$g17x5wlSiW1FZAh1Eudchw$ZeSAqj3Xak0io8v/pmPW0BX9EX5R2zFXDwbbD68oBFk=",
"last_login": null,
"is_superuser": true
},
{
"username": "admin",
"email": "dvadmin@django-vue-admin.com",
"mobile": "18888888888",
"avatar": "",
"name": "管理员",
"gender": 1,
"user_type": 0,
"role": [],
"dept_key": "dvadmin",
"first_name": "",
"last_name": "",
"is_staff": true,
"is_active": true,
"password": "pbkdf2_sha256$260000$g17x5wlSiW1FZAh1Eudchw$ZeSAqj3Xak0io8v/pmPW0BX9EX5R2zFXDwbbD68oBFk=",
"last_login": null,
"is_superuser": false
},
{
"username": "test",
"email": "dvadmin@django-vue-admin.com",
"mobile": "18888888888",
"avatar": "",
"name": "测试人员",
"gender": 1,
"user_type": 0,
"role": [],
"role_key": ["public"],
"dept_key": "technology",
"first_name": "",
"last_name": "",
"is_staff": true,
"is_active": true,
"password": "pbkdf2_sha256$260000$g17x5wlSiW1FZAh1Eudchw$ZeSAqj3Xak0io8v/pmPW0BX9EX5R2zFXDwbbD68oBFk=",
"last_login": null,
"is_superuser": false
}
]

View File

@ -0,0 +1,87 @@
# 初始化
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "application.settings")
django.setup()
from dvadmin.utils.core_initialize import CoreInitialize
from dvadmin.system.fixtures.initSerializer import (
UsersInitSerializer, DeptInitSerializer, RoleInitSerializer,
MenuInitSerializer, ApiWhiteListInitSerializer, DictionaryInitSerializer,
SystemConfigInitSerializer, RoleMenuInitSerializer, RoleMenuButtonInitSerializer
)
class Initialize(CoreInitialize):
def init_dept(self):
"""
初始化部门信息
"""
self.init_base(DeptInitSerializer, unique_fields=['name', 'parent','key'])
def init_role(self):
"""
初始化角色信息
"""
self.init_base(RoleInitSerializer, unique_fields=['key'])
def init_users(self):
"""
初始化用户信息
"""
self.init_base(UsersInitSerializer, unique_fields=['username'])
def init_menu(self):
"""
初始化菜单信息
"""
self.init_base(MenuInitSerializer, unique_fields=['name', 'web_path', 'component', 'component_name'])
def init_role_menu(self):
"""
初始化角色菜单信息
"""
self.init_base(RoleMenuInitSerializer, unique_fields=['role__key', 'menu__web_path', 'menu__component_name'])
def init_role_menu_button(self):
"""
初始化角色菜单按钮信息
"""
self.init_base(RoleMenuButtonInitSerializer, unique_fields=['role__key', 'menu_button__value'])
def init_api_white_list(self):
"""
初始API白名单
"""
self.init_base(ApiWhiteListInitSerializer, unique_fields=['url', 'method', ])
def init_dictionary(self):
"""
初始化字典表
"""
self.init_base(DictionaryInitSerializer, unique_fields=['value', 'parent', ])
def init_system_config(self):
"""
初始化系统配置表
"""
self.init_base(SystemConfigInitSerializer, unique_fields=['key', 'parent', ])
def run(self):
self.init_dept()
self.init_role()
self.init_users()
self.init_menu()
self.init_role_menu()
self.init_role_menu_button()
self.init_api_white_list()
self.init_dictionary()
self.init_system_config()
if __name__ == "__main__":
Initialize(app='dvadmin.system').run()

View File

View File

@ -0,0 +1,103 @@
import json
import logging
import os
import django
from django.db.models import QuerySet
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
django.setup()
from django.core.management.base import BaseCommand
from application.settings import BASE_DIR
from dvadmin.system.models import Menu, Users, Dept, Role, ApiWhiteList, Dictionary, SystemConfig, RoleMenuButtonPermission, RoleMenuPermission
from dvadmin.system.fixtures.initSerializer import UsersInitSerializer, DeptInitSerializer, RoleInitSerializer, \
MenuInitSerializer, ApiWhiteListInitSerializer, DictionaryInitSerializer, SystemConfigInitSerializer, \
RoleMenuInitSerializer, RoleMenuButtonInitSerializer
logger = logging.getLogger(__name__)
class Command(BaseCommand):
"""
生产初始化菜单: python3 manage.py generate_init_json 生成初始化的model名
例如
全部生成python3 manage.py generate_init_json
只生成某个model的 python3 manage.py generate_init_json users
"""
def serializer_data(self, serializer, query_set: QuerySet):
serializer = serializer(query_set, many=True)
data = json.loads(json.dumps(serializer.data, ensure_ascii=False))
with open(os.path.join(BASE_DIR, f'init_{query_set.model._meta.model_name}.json'), 'w',encoding='utf-8') as f:
json.dump(data, f, indent=4, ensure_ascii=False)
return
def add_arguments(self, parser):
parser.add_argument("generate_name", nargs="*", type=str, help="初始化生成的表名")
def generate_users(self):
self.serializer_data(UsersInitSerializer, Users.objects.all())
def generate_role(self):
self.serializer_data(RoleInitSerializer, Role.objects.all())
def generate_dept(self):
self.serializer_data(DeptInitSerializer, Dept.objects.filter(parent_id__isnull=True))
def generate_menu(self):
self.serializer_data(MenuInitSerializer, Menu.objects.filter(parent_id__isnull=True))
def generate_api_white_list(self):
self.serializer_data(ApiWhiteListInitSerializer, ApiWhiteList.objects.all())
def generate_dictionary(self):
self.serializer_data(DictionaryInitSerializer, Dictionary.objects.filter(parent_id__isnull=True))
def generate_system_config(self):
self.serializer_data(SystemConfigInitSerializer, SystemConfig.objects.filter(parent_id__isnull=True))
def generate_role_menu(self):
self.serializer_data(RoleMenuInitSerializer, RoleMenuPermission.objects.all())
def generate_role_menu_button(self):
self.serializer_data(RoleMenuButtonInitSerializer, RoleMenuButtonPermission.objects.all())
def handle(self, *args, **options):
generate_name = options.get('generate_name')
generate_name_dict = {
"users": self.generate_users,
"role": self.generate_role,
"dept": self.generate_dept,
"menu": self.generate_menu,
"api_white_list": self.generate_api_white_list,
"dictionary": self.generate_dictionary,
"system_config": self.generate_system_config,
"role_menu": self.generate_role_menu,
"role_menu_button": self.generate_role_menu_button,
}
if not generate_name:
for ele in generate_name_dict.keys():
generate_name_dict[ele]()
return
for generate_name in generate_name:
if generate_name not in generate_name_dict:
print(f"该初始化方法尚未配置\n{generate_name_dict}")
raise Exception(f"该初始化方法尚未配置,已配置项:{list(generate_name_dict.keys())}")
generate_name_dict[generate_name]()
return
if __name__ == '__main__':
# with open(os.path.join(BASE_DIR, 'temp_init_menu.json')) as f:
# for menu_data in json.load(f):
# menu_data['creator'] = 1
# menu_data['modifier'] = 1
# menu_data['dept_belong_id'] = 1
# request.user = Users.objects.order_by('create_datetime').first()
# serializer = MenuInitSerializer(data=menu_data, request=request)
# serializer.is_valid(raise_exception=True)
# serializer.save()
a = Users.objects.filter()
print(type(Users.objects.filter()))

View File

@ -0,0 +1,56 @@
import logging
from django.core.management.base import BaseCommand
from application import settings
logger = logging.getLogger(__name__)
class Command(BaseCommand):
"""
项目初始化命令: python manage.py init
"""
def add_arguments(self, parser):
parser.add_argument(
"init_name",
nargs="*",
type=str,
)
parser.add_argument("-y", nargs="*")
parser.add_argument("-Y", nargs="*")
parser.add_argument("-n", nargs="*")
parser.add_argument("-N", nargs="*")
parser.add_argument("-app", nargs="*")
parser.add_argument("-A", nargs="*")
def handle(self, *args, **options):
reset = False
if isinstance(options.get("y"), list) or isinstance(options.get("Y"), list):
reset = True
if isinstance(options.get("n"), list) or isinstance(options.get("N"), list):
reset = False
assign_apps = options.get("app") or options.get("A") or []
for app in settings.INSTALLED_APPS:
if assign_apps and app not in assign_apps:
continue
try:
exec(
f"""
from {app}.fixtures.initialize import Initialize
Initialize(reset={reset},app="{app}").run()
"""
)
except ModuleNotFoundError:
# 兼容之前版本初始化
try:
exec(
f"""
from {app}.initialize import main
main(reset={reset})
"""
)
except ModuleNotFoundError:
pass
print("初始化数据完成!")

View File

@ -0,0 +1,83 @@
# 城市联动
"""
到乡级 使用方法
1. https://www.npmjs.com/package/china-division 下载数据把对应的json放入对应目录
2. 修改此文件中对应json名
3. 右击执行此py文件进行初始化
"""
import json
import os
import django
import pypinyin
from django.core.management import BaseCommand
from django.db import connection
from application import dispatch
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
django.setup()
from application.settings import BASE_DIR
from dvadmin.system.models import Area
area_code_list = []
def area_list(code_list, pcode=None, depth=1):
"""
递归获取所有列表
"""
for code_dict in code_list:
code = code_dict.get('code', None)
name = code_dict.get('name', None)
children = code_dict.get('children', None)
pinyin = ''.join([''.join(i) for i in pypinyin.pinyin(name, style=pypinyin.NORMAL)])
area_code_list.append(
{
"name": name,
"code": code,
"level": depth,
"pinyin": pinyin,
"initials": pinyin[0].upper() if pinyin else "#",
"pcode_id": pcode,
}
)
if children:
area_list(code_list=children, pcode=code, depth=depth + 1)
def main():
with open(os.path.join(BASE_DIR, 'dvadmin', 'system', 'util', 'pca-code.json'), 'r', encoding="utf-8") as load_f:
code_list = json.load(load_f)
area_list(code_list)
if Area.objects.count() == 0:
Area.objects.bulk_create([Area(**ele) for ele in area_code_list])
else:
for ele in area_code_list:
code = ele.pop("code")
Area.objects.update_or_create(code=code, defaults=ele)
class Command(BaseCommand):
"""
项目初始化命令: python manage.py init
"""
def add_arguments(self, parser):
pass
def handle(self, *args, **options):
print(f"正在准备初始化省份数据...")
if dispatch.is_tenants_mode():
from django_tenants.utils import get_tenant_model
from django_tenants.utils import tenant_context
for tenant in get_tenant_model().objects.exclude(schema_name='public'):
with tenant_context(tenant):
print(f"租户[{connection.tenant.schema_name}]初始化数据开始...")
main()
print(f"租户[{connection.tenant.schema_name}]初始化数据完成!")
else:
main()
print("省份数据初始化数据完成!")

View File

@ -0,0 +1,574 @@
# Generated by Django 4.2.14 on 2025-10-16 01:27
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import dvadmin.system.models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='Users',
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('username', models.CharField(db_index=True, help_text='用户账号', max_length=150, unique=True, verbose_name='用户账号')),
('email', models.EmailField(blank=True, help_text='邮箱', max_length=255, null=True, verbose_name='邮箱')),
('mobile', models.CharField(blank=True, help_text='电话', max_length=255, null=True, verbose_name='电话')),
('avatar', models.CharField(blank=True, help_text='头像', max_length=255, null=True, verbose_name='头像')),
('name', models.CharField(help_text='姓名', max_length=40, verbose_name='姓名')),
('gender', models.IntegerField(blank=True, choices=[(0, '未知'), (1, ''), (2, '')], default=0, help_text='性别', null=True, verbose_name='性别')),
('user_type', models.IntegerField(blank=True, choices=[(0, '后台用户'), (1, '前台用户')], default=0, help_text='用户类型', null=True, verbose_name='用户类型')),
('login_error_count', models.IntegerField(default=0, help_text='登录错误次数', verbose_name='登录错误次数')),
('pwd_change_count', models.IntegerField(blank=True, default=0, help_text='密码修改次数', verbose_name='密码修改次数')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '用户表',
'verbose_name_plural': '用户表',
'db_table': 'dvadmin_system_users',
'ordering': ('-create_datetime',),
},
managers=[
('objects', dvadmin.system.models.CustomUserManager()),
],
),
migrations.CreateModel(
name='Dept',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('name', models.CharField(help_text='部门名称', max_length=64, verbose_name='部门名称')),
('key', models.CharField(blank=True, help_text='关联字符', max_length=64, null=True, unique=True, verbose_name='关联字符')),
('sort', models.IntegerField(default=1, help_text='显示排序', verbose_name='显示排序')),
('owner', models.CharField(blank=True, help_text='负责人', max_length=32, null=True, verbose_name='负责人')),
('phone', models.CharField(blank=True, help_text='联系电话', max_length=32, null=True, verbose_name='联系电话')),
('email', models.EmailField(blank=True, help_text='邮箱', max_length=32, null=True, verbose_name='邮箱')),
('status', models.BooleanField(blank=True, default=True, help_text='部门状态', null=True, verbose_name='部门状态')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('parent', models.ForeignKey(blank=True, db_constraint=False, default=None, help_text='上级部门', null=True, on_delete=django.db.models.deletion.CASCADE, to='system.dept', verbose_name='上级部门')),
],
options={
'verbose_name': '部门表',
'verbose_name_plural': '部门表',
'db_table': 'dvadmin_system_dept',
'ordering': ('sort',),
},
),
migrations.CreateModel(
name='Menu',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('icon', models.CharField(blank=True, help_text='菜单图标', max_length=64, null=True, verbose_name='菜单图标')),
('name', models.CharField(help_text='菜单名称', max_length=64, verbose_name='菜单名称')),
('sort', models.IntegerField(blank=True, default=1, help_text='显示排序', null=True, verbose_name='显示排序')),
('is_link', models.BooleanField(default=False, help_text='是否外链', verbose_name='是否外链')),
('link_url', models.CharField(blank=True, help_text='链接地址', max_length=255, null=True, verbose_name='链接地址')),
('is_catalog', models.BooleanField(default=False, help_text='是否目录', verbose_name='是否目录')),
('web_path', models.CharField(blank=True, help_text='路由地址', max_length=128, null=True, verbose_name='路由地址')),
('component', models.CharField(blank=True, help_text='组件地址', max_length=128, null=True, verbose_name='组件地址')),
('component_name', models.CharField(blank=True, help_text='组件名称', max_length=50, null=True, verbose_name='组件名称')),
('status', models.BooleanField(blank=True, default=True, help_text='菜单状态', verbose_name='菜单状态')),
('cache', models.BooleanField(blank=True, default=False, help_text='是否页面缓存', verbose_name='是否页面缓存')),
('visible', models.BooleanField(blank=True, default=True, help_text='侧边栏中是否显示', verbose_name='侧边栏中是否显示')),
('is_iframe', models.BooleanField(blank=True, default=False, help_text='框架外显示', verbose_name='框架外显示')),
('is_affix', models.BooleanField(blank=True, default=False, help_text='是否固定', verbose_name='是否固定')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('parent', models.ForeignKey(blank=True, db_constraint=False, help_text='上级菜单', null=True, on_delete=django.db.models.deletion.CASCADE, to='system.menu', verbose_name='上级菜单')),
],
options={
'verbose_name': '菜单表',
'verbose_name_plural': '菜单表',
'db_table': 'dvadmin_system_menu',
'ordering': ('sort',),
},
),
migrations.CreateModel(
name='MenuButton',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('name', models.CharField(help_text='名称', max_length=64, verbose_name='名称')),
('value', models.CharField(help_text='权限值', max_length=64, unique=True, verbose_name='权限值')),
('api', models.CharField(help_text='接口地址', max_length=200, verbose_name='接口地址')),
('method', models.IntegerField(blank=True, default=0, help_text='接口请求方法', null=True, verbose_name='接口请求方法')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('menu', models.ForeignKey(db_constraint=False, help_text='关联菜单', on_delete=django.db.models.deletion.CASCADE, related_name='menuPermission', to='system.menu', verbose_name='关联菜单')),
],
options={
'verbose_name': '菜单权限表',
'verbose_name_plural': '菜单权限表',
'db_table': 'dvadmin_system_menu_button',
'ordering': ('-name',),
},
),
migrations.CreateModel(
name='MessageCenter',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('title', models.CharField(help_text='标题', max_length=100, verbose_name='标题')),
('content', models.TextField(help_text='内容', verbose_name='内容')),
('target_type', models.IntegerField(default=0, help_text='目标类型', verbose_name='目标类型')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('target_dept', models.ManyToManyField(blank=True, db_constraint=False, help_text='目标部门', to='system.dept', verbose_name='目标部门')),
],
options={
'verbose_name': '消息中心',
'verbose_name_plural': '消息中心',
'db_table': 'dvadmin_message_center',
'ordering': ('-create_datetime',),
},
),
migrations.CreateModel(
name='Role',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('name', models.CharField(help_text='角色名称', max_length=64, verbose_name='角色名称')),
('key', models.CharField(help_text='权限字符', max_length=64, unique=True, verbose_name='权限字符')),
('sort', models.IntegerField(default=1, help_text='角色顺序', verbose_name='角色顺序')),
('status', models.BooleanField(default=True, help_text='角色状态', verbose_name='角色状态')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '角色表',
'verbose_name_plural': '角色表',
'db_table': 'dvadmin_system_role',
'ordering': ('sort',),
},
),
migrations.CreateModel(
name='RoleMenuPermission',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('menu', models.ForeignKey(db_constraint=False, help_text='关联菜单', on_delete=django.db.models.deletion.CASCADE, related_name='role_menu', to='system.menu', verbose_name='关联菜单')),
('role', models.ForeignKey(db_constraint=False, help_text='关联角色', on_delete=django.db.models.deletion.CASCADE, related_name='role_menu', to='system.role', verbose_name='关联角色')),
],
options={
'verbose_name': '角色菜单权限表',
'verbose_name_plural': '角色菜单权限表',
'db_table': 'dvadmin_role_menu_permission',
},
),
migrations.CreateModel(
name='RoleMenuButtonPermission',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('data_range', models.IntegerField(choices=[(0, '仅本人数据权限'), (1, '本部门及以下数据权限'), (2, '本部门数据权限'), (3, '全部数据权限'), (4, '自定数据权限')], default=0, help_text='数据权限范围', verbose_name='数据权限范围')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('dept', models.ManyToManyField(blank=True, db_constraint=False, help_text='数据权限-关联部门', to='system.dept', verbose_name='数据权限-关联部门')),
('menu_button', models.ForeignKey(blank=True, db_constraint=False, help_text='关联菜单按钮', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='menu_button_permission', to='system.menubutton', verbose_name='关联菜单按钮')),
('role', models.ForeignKey(db_constraint=False, help_text='关联角色', on_delete=django.db.models.deletion.CASCADE, related_name='role_menu_button', to='system.role', verbose_name='关联角色')),
],
options={
'verbose_name': '角色按钮权限表',
'verbose_name_plural': '角色按钮权限表',
'db_table': 'dvadmin_role_menu_button_permission',
'ordering': ('-create_datetime',),
},
),
migrations.CreateModel(
name='Post',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('name', models.CharField(help_text='岗位名称', max_length=64, verbose_name='岗位名称')),
('code', models.CharField(help_text='岗位编码', max_length=32, verbose_name='岗位编码')),
('sort', models.IntegerField(default=1, help_text='岗位顺序', verbose_name='岗位顺序')),
('status', models.IntegerField(choices=[(0, '离职'), (1, '在职')], default=1, help_text='岗位状态', verbose_name='岗位状态')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '岗位表',
'verbose_name_plural': '岗位表',
'db_table': 'dvadmin_system_post',
'ordering': ('sort',),
},
),
migrations.CreateModel(
name='OperationLog',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('request_modular', models.CharField(blank=True, help_text='请求模块', max_length=64, null=True, verbose_name='请求模块')),
('request_path', models.CharField(blank=True, help_text='请求地址', max_length=400, null=True, verbose_name='请求地址')),
('request_body', models.TextField(blank=True, help_text='请求参数', null=True, verbose_name='请求参数')),
('request_method', models.CharField(blank=True, help_text='请求方式', max_length=8, null=True, verbose_name='请求方式')),
('request_msg', models.TextField(blank=True, help_text='操作说明', null=True, verbose_name='操作说明')),
('request_ip', models.CharField(blank=True, help_text='请求ip地址', max_length=32, null=True, verbose_name='请求ip地址')),
('request_browser', models.CharField(blank=True, help_text='请求浏览器', max_length=64, null=True, verbose_name='请求浏览器')),
('response_code', models.CharField(blank=True, help_text='响应状态码', max_length=32, null=True, verbose_name='响应状态码')),
('request_os', models.CharField(blank=True, help_text='操作系统', max_length=64, null=True, verbose_name='操作系统')),
('json_result', models.TextField(blank=True, help_text='返回信息', null=True, verbose_name='返回信息')),
('status', models.BooleanField(default=False, help_text='响应状态', verbose_name='响应状态')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '操作日志',
'verbose_name_plural': '操作日志',
'db_table': 'dvadmin_system_operation_log',
'ordering': ('-create_datetime',),
},
),
migrations.CreateModel(
name='MessageCenterTargetUser',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('is_read', models.BooleanField(blank=True, default=False, help_text='是否已读', null=True, verbose_name='是否已读')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('messagecenter', models.ForeignKey(db_constraint=False, help_text='关联消息中心表', on_delete=django.db.models.deletion.CASCADE, to='system.messagecenter', verbose_name='关联消息中心表')),
('users', models.ForeignKey(db_constraint=False, help_text='关联用户表', on_delete=django.db.models.deletion.CASCADE, related_name='target_user', to=settings.AUTH_USER_MODEL, verbose_name='关联用户表')),
],
options={
'verbose_name': '消息中心目标用户表',
'verbose_name_plural': '消息中心目标用户表',
'db_table': 'dvadmin_message_center_target_user',
},
),
migrations.AddField(
model_name='messagecenter',
name='target_role',
field=models.ManyToManyField(blank=True, db_constraint=False, help_text='目标角色', to='system.role', verbose_name='目标角色'),
),
migrations.AddField(
model_name='messagecenter',
name='target_user',
field=models.ManyToManyField(blank=True, help_text='目标用户', related_name='user', through='system.MessageCenterTargetUser', to=settings.AUTH_USER_MODEL, verbose_name='目标用户'),
),
migrations.CreateModel(
name='MenuField',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('model', models.CharField(max_length=64, verbose_name='表名')),
('field_name', models.CharField(max_length=64, verbose_name='模型表字段名')),
('title', models.CharField(max_length=64, verbose_name='字段显示名')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('menu', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='system.menu', verbose_name='菜单')),
],
options={
'verbose_name': '菜单字段表',
'verbose_name_plural': '菜单字段表',
'db_table': 'dvadmin_system_menu_field',
'ordering': ('id',),
},
),
migrations.CreateModel(
name='LoginLog',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('username', models.CharField(blank=True, help_text='登录用户名', max_length=32, null=True, verbose_name='登录用户名')),
('ip', models.CharField(blank=True, help_text='登录ip', max_length=32, null=True, verbose_name='登录ip')),
('agent', models.TextField(blank=True, help_text='agent信息', null=True, verbose_name='agent信息')),
('browser', models.CharField(blank=True, help_text='浏览器名', max_length=200, null=True, verbose_name='浏览器名')),
('os', models.CharField(blank=True, help_text='操作系统', max_length=200, null=True, verbose_name='操作系统')),
('continent', models.CharField(blank=True, help_text='', max_length=50, null=True, verbose_name='')),
('country', models.CharField(blank=True, help_text='国家', max_length=50, null=True, verbose_name='国家')),
('province', models.CharField(blank=True, help_text='省份', max_length=50, null=True, verbose_name='省份')),
('city', models.CharField(blank=True, help_text='城市', max_length=50, null=True, verbose_name='城市')),
('district', models.CharField(blank=True, help_text='县区', max_length=50, null=True, verbose_name='县区')),
('isp', models.CharField(blank=True, help_text='运营商', max_length=50, null=True, verbose_name='运营商')),
('area_code', models.CharField(blank=True, help_text='区域代码', max_length=50, null=True, verbose_name='区域代码')),
('country_english', models.CharField(blank=True, help_text='英文全称', max_length=50, null=True, verbose_name='英文全称')),
('country_code', models.CharField(blank=True, help_text='简称', max_length=50, null=True, verbose_name='简称')),
('longitude', models.CharField(blank=True, help_text='经度', max_length=50, null=True, verbose_name='经度')),
('latitude', models.CharField(blank=True, help_text='纬度', max_length=50, null=True, verbose_name='纬度')),
('login_type', models.IntegerField(choices=[(1, '普通登录'), (2, '微信扫码登录')], default=1, help_text='登录类型', verbose_name='登录类型')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '登录日志',
'verbose_name_plural': '登录日志',
'db_table': 'dvadmin_system_login_log',
'ordering': ('-create_datetime',),
},
),
migrations.CreateModel(
name='FileList',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('name', models.CharField(blank=True, help_text='名称', max_length=200, null=True, verbose_name='名称')),
('url', models.FileField(blank=True, null=True, upload_to=dvadmin.system.models.media_file_name)),
('file_url', models.CharField(blank=True, help_text='文件地址', max_length=255, verbose_name='文件地址')),
('engine', models.CharField(blank=True, default='local', help_text='引擎', max_length=100, verbose_name='引擎')),
('mime_type', models.CharField(blank=True, help_text='Mime类型', max_length=100, verbose_name='Mime类型')),
('size', models.CharField(blank=True, help_text='文件大小', max_length=36, verbose_name='文件大小')),
('md5sum', models.CharField(blank=True, help_text='文件md5', max_length=36, verbose_name='文件md5')),
('upload_method', models.SmallIntegerField(blank=True, choices=[(0, '默认上传'), (1, '文件选择器上传')], default=0, help_text='上传方式', null=True, verbose_name='上传方式')),
('file_type', models.SmallIntegerField(blank=True, choices=[(0, '图片'), (1, '视频'), (2, '音频'), (3, '其他')], default=3, help_text='文件类型', null=True, verbose_name='文件类型')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '文件管理',
'verbose_name_plural': '文件管理',
'db_table': 'dvadmin_system_file_list',
'ordering': ('-create_datetime',),
},
),
migrations.CreateModel(
name='FieldPermission',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('is_query', models.BooleanField(default=1, verbose_name='是否可查询')),
('is_create', models.BooleanField(default=1, verbose_name='是否可创建')),
('is_update', models.BooleanField(default=1, verbose_name='是否可更新')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('field', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, related_name='menu_field', to='system.menufield', verbose_name='字段')),
('role', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to='system.role', verbose_name='角色')),
],
options={
'verbose_name': '字段权限表',
'verbose_name_plural': '字段权限表',
'db_table': 'dvadmin_system_field_permission',
'ordering': ('id',),
},
),
migrations.CreateModel(
name='DownloadCenter',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('task_name', models.CharField(help_text='任务名称', max_length=255, verbose_name='任务名称')),
('task_status', models.SmallIntegerField(choices=[(0, '任务已创建'), (1, '任务进行中'), (2, '任务完成'), (3, '任务失败')], default=0, help_text='是否可下载', verbose_name='是否可下载')),
('file_name', models.CharField(blank=True, help_text='文件名', max_length=255, null=True, verbose_name='文件名')),
('url', models.FileField(blank=True, null=True, upload_to=dvadmin.system.models.media_file_name_downloadcenter)),
('size', models.BigIntegerField(default=0, help_text='文件大小', verbose_name='文件大小')),
('md5sum', models.CharField(blank=True, help_text='文件md5', max_length=36, null=True, verbose_name='文件md5')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '下载中心',
'verbose_name_plural': '下载中心',
'db_table': 'dvadmin_download_center',
'ordering': ('-create_datetime',),
},
),
migrations.CreateModel(
name='Dictionary',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('label', models.CharField(blank=True, help_text='字典名称', max_length=100, null=True, verbose_name='字典名称')),
('value', models.CharField(blank=True, help_text='字典编号/实际值', max_length=200, null=True, verbose_name='字典编号')),
('type', models.IntegerField(choices=[(0, 'text'), (1, 'number'), (2, 'date'), (3, 'datetime'), (4, 'time'), (5, 'files'), (6, 'boolean'), (7, 'images')], default=0, help_text='数据值类型', verbose_name='数据值类型')),
('color', models.CharField(blank=True, help_text='颜色', max_length=20, null=True, verbose_name='颜色')),
('is_value', models.BooleanField(default=False, help_text='是否为value值,用来做具体值存放', verbose_name='是否为value值')),
('status', models.BooleanField(default=True, help_text='状态', verbose_name='状态')),
('sort', models.IntegerField(blank=True, default=1, help_text='显示排序', null=True, verbose_name='显示排序')),
('remark', models.CharField(blank=True, help_text='备注', max_length=2000, null=True, verbose_name='备注')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('parent', models.ForeignKey(blank=True, db_constraint=False, help_text='父级', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sublist', to='system.dictionary', verbose_name='父级')),
],
options={
'verbose_name': '字典表',
'verbose_name_plural': '字典表',
'db_table': 'dvadmin_system_dictionary',
'ordering': ('sort',),
},
),
migrations.CreateModel(
name='Area',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('name', models.CharField(help_text='名称', max_length=100, verbose_name='名称')),
('code', models.CharField(db_index=True, help_text='地区编码', max_length=20, unique=True, verbose_name='地区编码')),
('level', models.BigIntegerField(help_text='地区层级(1省份 2城市 3区县 4乡级)', verbose_name='地区层级(1省份 2城市 3区县 4乡级)')),
('pinyin', models.CharField(help_text='拼音', max_length=255, verbose_name='拼音')),
('initials', models.CharField(help_text='首字母', max_length=20, verbose_name='首字母')),
('enable', models.BooleanField(default=True, help_text='是否启用', verbose_name='是否启用')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('pcode', models.ForeignKey(blank=True, db_constraint=False, help_text='父地区编码', null=True, on_delete=django.db.models.deletion.CASCADE, to='system.area', to_field='code', verbose_name='父地区编码')),
],
options={
'verbose_name': '地区表',
'verbose_name_plural': '地区表',
'db_table': 'dvadmin_system_area',
'ordering': ('code',),
},
),
migrations.CreateModel(
name='ApiWhiteList',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('url', models.CharField(help_text='url地址', max_length=200, verbose_name='url')),
('method', models.IntegerField(blank=True, default=0, help_text='接口请求方法', null=True, verbose_name='接口请求方法')),
('enable_datasource', models.BooleanField(blank=True, default=True, help_text='激活数据权限', verbose_name='激活数据权限')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'verbose_name': '接口白名单',
'verbose_name_plural': '接口白名单',
'db_table': 'dvadmin_api_white_list',
'ordering': ('-create_datetime',),
},
),
migrations.AddField(
model_name='users',
name='current_role',
field=models.ForeignKey(blank=True, db_constraint=False, help_text='当前登录角色', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='current_role_set', to='system.role', verbose_name='当前登录角色'),
),
migrations.AddField(
model_name='users',
name='dept',
field=models.ForeignKey(blank=True, db_constraint=False, help_text='关联部门', null=True, on_delete=django.db.models.deletion.PROTECT, to='system.dept', verbose_name='所属部门'),
),
migrations.AddField(
model_name='users',
name='groups',
field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups'),
),
migrations.AddField(
model_name='users',
name='manage_dept',
field=models.ManyToManyField(blank=True, db_constraint=False, help_text='管理部门', related_name='manage_dept_set', to='system.dept', verbose_name='管理部门'),
),
migrations.AddField(
model_name='users',
name='post',
field=models.ManyToManyField(blank=True, db_constraint=False, help_text='关联岗位', to='system.post', verbose_name='关联岗位'),
),
migrations.AddField(
model_name='users',
name='role',
field=models.ManyToManyField(blank=True, db_constraint=False, help_text='关联角色', to='system.role', verbose_name='关联角色'),
),
migrations.AddField(
model_name='users',
name='user_permissions',
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions'),
),
migrations.CreateModel(
name='SystemConfig',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('description', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('modifier', models.CharField(blank=True, help_text='修改人', max_length=255, null=True, verbose_name='修改人')),
('dept_belong_id', models.CharField(blank=True, help_text='数据归属部门', max_length=255, null=True, verbose_name='数据归属部门')),
('update_datetime', models.DateTimeField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateTimeField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('title', models.CharField(help_text='标题', max_length=50, verbose_name='标题')),
('key', models.CharField(db_index=True, help_text='', max_length=100, verbose_name='')),
('value', models.JSONField(blank=True, help_text='', max_length=100, null=True, verbose_name='')),
('sort', models.IntegerField(blank=True, default=0, help_text='排序', verbose_name='排序')),
('status', models.BooleanField(default=True, help_text='启用状态', verbose_name='启用状态')),
('data_options', models.JSONField(blank=True, help_text='数据options', null=True, verbose_name='数据options')),
('form_item_type', models.IntegerField(blank=True, choices=[(0, 'text'), (1, 'datetime'), (2, 'date'), (3, 'textarea'), (4, 'select'), (5, 'checkbox'), (6, 'radio'), (7, 'img'), (8, 'file'), (9, 'switch'), (10, 'number'), (11, 'array'), (12, 'imgs'), (13, 'foreignkey'), (14, 'manytomany'), (15, 'time')], default=0, help_text='表单类型', verbose_name='表单类型')),
('rule', models.JSONField(blank=True, help_text='校验规则', null=True, verbose_name='校验规则')),
('placeholder', models.CharField(blank=True, help_text='提示信息', max_length=50, null=True, verbose_name='提示信息')),
('setting', models.JSONField(blank=True, help_text='配置', null=True, verbose_name='配置')),
('creator', models.ForeignKey(db_constraint=False, help_text='创建人', null=True, on_delete=django.db.models.deletion.SET_NULL, related_query_name='creator_query', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('parent', models.ForeignKey(blank=True, db_constraint=False, help_text='父级', null=True, on_delete=django.db.models.deletion.CASCADE, to='system.systemconfig', verbose_name='父级')),
],
options={
'verbose_name': '系统配置表',
'verbose_name_plural': '系统配置表',
'db_table': 'dvadmin_system_config',
'ordering': ('sort',),
'unique_together': {('key', 'parent_id')},
},
),
]

View File

687
dvadmin/system/models.py Normal file
View File

@ -0,0 +1,687 @@
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
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from application import dispatch
from dvadmin.utils.models import CoreModel, table_prefix, get_custom_app_models
class Role(CoreModel):
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
class Meta:
db_table = table_prefix + "system_role"
verbose_name = "角色表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class CustomUserManager(UserManager):
def create_superuser(self, username, email=None, password=None, **extra_fields):
user = super(CustomUserManager, self).create_superuser(username, email, password, **extra_fields)
user.set_password(password)
try:
user.role.add(Role.objects.get(name="管理员"))
user.save(using=self._db)
return user
except ObjectDoesNotExist:
user.delete()
raise ValidationError("角色`管理员`不存在, 创建失败, 请先执行python manage.py init")
class Users(CoreModel, AbstractUser):
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
help_text="用户账号")
email = models.EmailField(max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话")
avatar = models.CharField(max_length=255, verbose_name="头像", null=True, blank=True, help_text="头像")
name = models.CharField(max_length=40, verbose_name="姓名", help_text="姓名")
GENDER_CHOICES = (
(0, "未知"),
(1, ""),
(2, ""),
)
gender = models.IntegerField(
choices=GENDER_CHOICES, default=0, verbose_name="性别", null=True, blank=True, help_text="性别"
)
USER_TYPE = (
(0, "后台用户"),
(1, "前台用户"),
)
user_type = models.IntegerField(
choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True, help_text="用户类型"
)
post = models.ManyToManyField(to="Post", blank=True, verbose_name="关联岗位", db_constraint=False,
help_text="关联岗位")
role = models.ManyToManyField(to="Role", blank=True, verbose_name="关联角色", db_constraint=False,
help_text="关联角色")
current_role = models.ForeignKey(to=Role, null=True, blank=True, db_constraint=False, on_delete=models.SET_NULL,
verbose_name="当前登录角色", help_text="当前登录角色", related_name='current_role_set')
dept = models.ForeignKey(
to="Dept",
verbose_name="所属部门",
on_delete=models.PROTECT,
db_constraint=False,
null=True,
blank=True,
help_text="关联部门",
)
manage_dept = models.ManyToManyField(
to="Dept",
verbose_name="管理部门",
db_constraint=False,
blank=True,
help_text="管理部门",
related_name='manage_dept_set'
)
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):
if raw_password:
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
def save(self, *args, **kwargs):
if self.name == "":
self.name = self.username
super().save(*args, **kwargs)
class Meta:
db_table = table_prefix + "system_users"
verbose_name = "用户表"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Post(CoreModel):
name = models.CharField(null=False, max_length=64, verbose_name="岗位名称", help_text="岗位名称")
code = models.CharField(max_length=32, verbose_name="岗位编码", help_text="岗位编码")
sort = models.IntegerField(default=1, verbose_name="岗位顺序", help_text="岗位顺序")
STATUS_CHOICES = (
(0, "离职"),
(1, "在职"),
)
status = models.IntegerField(choices=STATUS_CHOICES, default=1, verbose_name="岗位状态", help_text="岗位状态")
class Meta:
db_table = table_prefix + "system_post"
verbose_name = "岗位表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class Dept(CoreModel):
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符", help_text="关联字符")
sort = models.IntegerField(default=1, verbose_name="显示排序", help_text="显示排序")
owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人")
phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话")
email = models.EmailField(max_length=32, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
status = models.BooleanField(default=True, verbose_name="部门状态", null=True, blank=True, help_text="部门状态")
parent = models.ForeignKey(
to="Dept",
on_delete=models.CASCADE,
default=None,
verbose_name="上级部门",
db_constraint=False,
null=True,
blank=True,
help_text="上级部门",
)
@classmethod
def _recursion(cls, instance, parent, result):
new_instance = getattr(instance, parent, None)
res = []
data = getattr(instance, result, None)
if data:
res.append(data)
if new_instance:
array = cls._recursion(new_instance, parent, result)
res += array
return res
@classmethod
def get_region_name(cls, obj):
"""
获取某个用户的递归所有部门名称
"""
dept_name_all = cls._recursion(obj, "parent", "name")
dept_name_all.reverse()
return "/".join(dept_name_all)
@classmethod
def recursion_all_dept(cls, dept_id: int, dept_all_list=None, dept_list=None):
"""
递归获取部门的所有下级部门
:param dept_id: 需要获取的id
:param dept_all_list: 所有列表
:param dept_list: 递归list
:return:
"""
if not dept_all_list:
dept_all_list = Dept.objects.values("id", "parent")
if dept_list is None:
dept_list = [dept_id]
for ele in dept_all_list:
if ele.get("parent") == dept_id:
dept_list.append(ele.get("id"))
cls.recursion_all_dept(ele.get("id"), dept_all_list, dept_list)
return list(set(dept_list))
class Meta:
db_table = table_prefix + "system_dept"
verbose_name = "部门表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class Menu(CoreModel):
parent = models.ForeignKey(
to="Menu",
on_delete=models.CASCADE,
verbose_name="上级菜单",
null=True,
blank=True,
db_constraint=False,
help_text="上级菜单",
)
icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称")
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
ISLINK_CHOICES = (
(0, ""),
(1, ""),
)
is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
link_url = models.CharField(max_length=255, verbose_name="链接地址", null=True, blank=True, help_text="链接地址")
is_catalog = models.BooleanField(default=False, verbose_name="是否目录", help_text="是否目录")
web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
help_text="组件名称")
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
help_text="侧边栏中是否显示")
is_iframe = models.BooleanField(default=False, blank=True, verbose_name="框架外显示", help_text="框架外显示")
is_affix = models.BooleanField(default=False, blank=True, verbose_name="是否固定", help_text="是否固定")
@classmethod
def get_all_parent(cls, id: int, all_list=None, nodes=None):
"""
递归获取给定ID的所有层级
:param id: 参数ID
:param all_list: 所有列表
:param nodes: 递归列表
:return: nodes
"""
if not all_list:
all_list = Menu.objects.values("id", "name", "parent")
if nodes is None:
nodes = []
for ele in all_list:
if ele.get("id") == id:
parent_id = ele.get("parent")
if parent_id is not None:
cls.get_all_parent(parent_id, all_list, nodes)
nodes.append(ele)
return nodes
class Meta:
db_table = table_prefix + "system_menu"
verbose_name = "菜单表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class MenuField(CoreModel):
model = models.CharField(max_length=64, verbose_name='表名')
menu = models.ForeignKey(to='Menu', on_delete=models.CASCADE, verbose_name='菜单', db_constraint=False)
field_name = models.CharField(max_length=64, verbose_name='模型表字段名')
title = models.CharField(max_length=64, verbose_name='字段显示名')
class Meta:
db_table = table_prefix + "system_menu_field"
verbose_name = "菜单字段表"
verbose_name_plural = verbose_name
ordering = ("id",)
class FieldPermission(CoreModel):
role = models.ForeignKey(to='Role', on_delete=models.CASCADE, verbose_name='角色', db_constraint=False)
field = models.ForeignKey(to='MenuField', on_delete=models.CASCADE,related_name='menu_field', verbose_name='字段', db_constraint=False)
is_query = models.BooleanField(default=1, verbose_name='是否可查询')
is_create = models.BooleanField(default=1, verbose_name='是否可创建')
is_update = models.BooleanField(default=1, verbose_name='是否可更新')
class Meta:
db_table = table_prefix + "system_field_permission"
verbose_name = "字段权限表"
verbose_name_plural = verbose_name
ordering = ("id",)
class MenuButton(CoreModel):
menu = models.ForeignKey(
to="Menu",
db_constraint=False,
related_name="menuPermission",
on_delete=models.CASCADE,
verbose_name="关联菜单",
help_text="关联菜单",
)
name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
value = models.CharField(unique=True, max_length=64, verbose_name="权限值", help_text="权限值")
api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址")
METHOD_CHOICES = (
(0, "GET"),
(1, "POST"),
(2, "PUT"),
(3, "DELETE"),
)
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
help_text="接口请求方法")
class Meta:
db_table = table_prefix + "system_menu_button"
verbose_name = "菜单权限表"
verbose_name_plural = verbose_name
ordering = ("-name",)
class RoleMenuPermission(CoreModel):
role = models.ForeignKey(
to="Role",
db_constraint=False,
related_name="role_menu",
on_delete=models.CASCADE,
verbose_name="关联角色",
help_text="关联角色",
)
menu = models.ForeignKey(
to="Menu",
db_constraint=False,
related_name="role_menu",
on_delete=models.CASCADE,
verbose_name="关联菜单",
help_text="关联菜单",
)
class Meta:
db_table = table_prefix + "role_menu_permission"
verbose_name = "角色菜单权限表"
verbose_name_plural = verbose_name
# ordering = ("-create_datetime",)
class RoleMenuButtonPermission(CoreModel):
role = models.ForeignKey(
to="Role",
db_constraint=False,
related_name="role_menu_button",
on_delete=models.CASCADE,
verbose_name="关联角色",
help_text="关联角色",
)
menu_button = models.ForeignKey(
to="MenuButton",
db_constraint=False,
related_name="menu_button_permission",
on_delete=models.CASCADE,
verbose_name="关联菜单按钮",
help_text="关联菜单按钮",
null=True,
blank=True
)
DATASCOPE_CHOICES = (
(0, "仅本人数据权限"),
(1, "本部门及以下数据权限"),
(2, "本部门数据权限"),
(3, "全部数据权限"),
(4, "自定数据权限"),
)
data_range = models.IntegerField(default=0, choices=DATASCOPE_CHOICES, verbose_name="数据权限范围",
help_text="数据权限范围")
dept = models.ManyToManyField(to="Dept", blank=True, verbose_name="数据权限-关联部门", db_constraint=False,
help_text="数据权限-关联部门")
class Meta:
db_table = table_prefix + "role_menu_button_permission"
verbose_name = "角色按钮权限表"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Dictionary(CoreModel):
TYPE_LIST = (
(0, "text"),
(1, "number"),
(2, "date"),
(3, "datetime"),
(4, "time"),
(5, "files"),
(6, "boolean"),
(7, "images"),
)
label = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称")
value = models.CharField(max_length=200, blank=True, null=True, verbose_name="字典编号", help_text="字典编号/实际值")
parent = models.ForeignKey(
to="self",
related_name="sublist",
db_constraint=False,
on_delete=models.PROTECT,
blank=True,
null=True,
verbose_name="父级",
help_text="父级",
)
type = models.IntegerField(choices=TYPE_LIST, default=0, verbose_name="数据值类型", help_text="数据值类型")
color = models.CharField(max_length=20, blank=True, null=True, verbose_name="颜色", help_text="颜色")
is_value = models.BooleanField(default=False, verbose_name="是否为value值",
help_text="是否为value值,用来做具体值存放")
status = models.BooleanField(default=True, verbose_name="状态", help_text="状态")
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
remark = models.CharField(max_length=2000, blank=True, null=True, verbose_name="备注", help_text="备注")
class Meta:
db_table = table_prefix + "system_dictionary"
verbose_name = "字典表"
verbose_name_plural = verbose_name
ordering = ("sort",)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
super().save(force_insert, force_update, using, update_fields)
dispatch.refresh_dictionary() # 有更新则刷新字典配置
def delete(self, using=None, keep_parents=False):
res = super().delete(using, keep_parents)
dispatch.refresh_dictionary()
return res
class OperationLog(CoreModel):
request_modular = models.CharField(max_length=64, verbose_name="请求模块", null=True, blank=True,
help_text="请求模块")
request_path = models.CharField(max_length=400, verbose_name="请求地址", null=True, blank=True,
help_text="请求地址")
request_body = models.TextField(verbose_name="请求参数", null=True, blank=True, help_text="请求参数")
request_method = models.CharField(max_length=8, verbose_name="请求方式", null=True, blank=True,
help_text="请求方式")
request_msg = models.TextField(verbose_name="操作说明", null=True, blank=True, help_text="操作说明")
request_ip = models.CharField(max_length=32, verbose_name="请求ip地址", null=True, blank=True,
help_text="请求ip地址")
request_browser = models.CharField(max_length=64, verbose_name="请求浏览器", null=True, blank=True,
help_text="请求浏览器")
response_code = models.CharField(max_length=32, verbose_name="响应状态码", null=True, blank=True,
help_text="响应状态码")
request_os = models.CharField(max_length=64, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
json_result = models.TextField(verbose_name="返回信息", null=True, blank=True, help_text="返回信息")
status = models.BooleanField(default=False, verbose_name="响应状态", help_text="响应状态")
class Meta:
db_table = table_prefix + "system_operation_log"
verbose_name = "操作日志"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
def media_file_name(instance, filename):
h = instance.md5sum
basename, ext = os.path.splitext(filename)
return os.path.join("files", h[:1], h[1:2], h + ext.lower())
class FileList(CoreModel):
name = models.CharField(max_length=200, null=True, blank=True, verbose_name="名称", help_text="名称")
url = models.FileField(upload_to=media_file_name, null=True, blank=True,)
file_url = models.CharField(max_length=255, blank=True, verbose_name="文件地址", help_text="文件地址")
engine = models.CharField(max_length=100, default='local', blank=True, verbose_name="引擎", help_text="引擎")
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
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
if not self.file_url:
url = media_file_name(self, self.name)
self.file_url = f'media/{url}'
super(FileList, self).save(*args, **kwargs)
class Meta:
db_table = table_prefix + "system_file_list"
verbose_name = "文件管理"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Area(CoreModel):
name = models.CharField(max_length=100, verbose_name="名称", help_text="名称")
code = models.CharField(max_length=20, verbose_name="地区编码", help_text="地区编码", unique=True, db_index=True)
level = models.BigIntegerField(verbose_name="地区层级(1省份 2城市 3区县 4乡级)",
help_text="地区层级(1省份 2城市 3区县 4乡级)")
pinyin = models.CharField(max_length=255, verbose_name="拼音", help_text="拼音")
initials = models.CharField(max_length=20, verbose_name="首字母", help_text="首字母")
enable = models.BooleanField(default=True, verbose_name="是否启用", help_text="是否启用")
pcode = models.ForeignKey(
to="self",
verbose_name="父地区编码",
to_field="code",
on_delete=models.CASCADE,
db_constraint=False,
null=True,
blank=True,
help_text="父地区编码",
)
class Meta:
db_table = table_prefix + "system_area"
verbose_name = "地区表"
verbose_name_plural = verbose_name
ordering = ("code",)
def __str__(self):
return f"{self.name}"
class ApiWhiteList(CoreModel):
url = models.CharField(max_length=200, help_text="url地址", verbose_name="url")
METHOD_CHOICES = (
(0, "GET"),
(1, "POST"),
(2, "PUT"),
(3, "DELETE"),
)
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
help_text="接口请求方法")
enable_datasource = models.BooleanField(default=True, verbose_name="激活数据权限", help_text="激活数据权限",
blank=True)
class Meta:
db_table = table_prefix + "api_white_list"
verbose_name = "接口白名单"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class SystemConfig(CoreModel):
parent = models.ForeignKey(
to="self",
verbose_name="父级",
on_delete=models.CASCADE,
db_constraint=False,
null=True,
blank=True,
help_text="父级",
)
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
key = models.CharField(max_length=100, verbose_name="", help_text="", db_index=True)
value = models.JSONField(max_length=100, verbose_name="", help_text="", null=True, blank=True)
sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
data_options = models.JSONField(verbose_name="数据options", help_text="数据options", null=True, blank=True)
FORM_ITEM_TYPE_LIST = (
(0, "text"),
(1, "datetime"),
(2, "date"),
(3, "textarea"),
(4, "select"),
(5, "checkbox"),
(6, "radio"),
(7, "img"),
(8, "file"),
(9, "switch"),
(10, "number"),
(11, "array"),
(12, "imgs"),
(13, "foreignkey"),
(14, "manytomany"),
(15, "time"),
)
form_item_type = models.IntegerField(
choices=FORM_ITEM_TYPE_LIST, verbose_name="表单类型", help_text="表单类型", default=0, blank=True
)
rule = models.JSONField(null=True, blank=True, verbose_name="校验规则", help_text="校验规则")
placeholder = models.CharField(max_length=50, null=True, blank=True, verbose_name="提示信息", help_text="提示信息")
setting = models.JSONField(null=True, blank=True, verbose_name="配置", help_text="配置")
class Meta:
db_table = table_prefix + "system_config"
verbose_name = "系统配置表"
verbose_name_plural = verbose_name
ordering = ("sort",)
unique_together = (("key", "parent_id"),)
def __str__(self):
return f"{self.title}"
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
super().save(force_insert, force_update, using, update_fields)
dispatch.refresh_system_config() # 有更新则刷新系统配置
def delete(self, using=None, keep_parents=False):
res = super().delete(using, keep_parents)
dispatch.refresh_system_config()
return res
class LoginLog(CoreModel):
LOGIN_TYPE_CHOICES = ((1, "普通登录"), (2, "微信扫码登录"),)
username = models.CharField(max_length=32, verbose_name="登录用户名", null=True, blank=True, help_text="登录用户名")
ip = models.CharField(max_length=32, verbose_name="登录ip", null=True, blank=True, help_text="登录ip")
agent = models.TextField(verbose_name="agent信息", null=True, blank=True, help_text="agent信息")
browser = models.CharField(max_length=200, verbose_name="浏览器名", null=True, blank=True, help_text="浏览器名")
os = models.CharField(max_length=200, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
continent = models.CharField(max_length=50, verbose_name="", null=True, blank=True, help_text="")
country = models.CharField(max_length=50, verbose_name="国家", null=True, blank=True, help_text="国家")
province = models.CharField(max_length=50, verbose_name="省份", null=True, blank=True, help_text="省份")
city = models.CharField(max_length=50, verbose_name="城市", null=True, blank=True, help_text="城市")
district = models.CharField(max_length=50, verbose_name="县区", null=True, blank=True, help_text="县区")
isp = models.CharField(max_length=50, verbose_name="运营商", null=True, blank=True, help_text="运营商")
area_code = models.CharField(max_length=50, verbose_name="区域代码", null=True, blank=True, help_text="区域代码")
country_english = models.CharField(max_length=50, verbose_name="英文全称", null=True, blank=True,
help_text="英文全称")
country_code = models.CharField(max_length=50, verbose_name="简称", null=True, blank=True, help_text="简称")
longitude = models.CharField(max_length=50, verbose_name="经度", null=True, blank=True, help_text="经度")
latitude = models.CharField(max_length=50, verbose_name="纬度", null=True, blank=True, help_text="纬度")
login_type = models.IntegerField(default=1, choices=LOGIN_TYPE_CHOICES, verbose_name="登录类型",
help_text="登录类型")
class Meta:
db_table = table_prefix + "system_login_log"
verbose_name = "登录日志"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class MessageCenter(CoreModel):
title = models.CharField(max_length=100, verbose_name="标题", help_text="标题")
content = models.TextField(verbose_name="内容", help_text="内容")
target_type = models.IntegerField(default=0, verbose_name="目标类型", help_text="目标类型")
target_user = models.ManyToManyField(to=Users, related_name='user', through='MessageCenterTargetUser',
through_fields=('messagecenter', 'users'), blank=True, verbose_name="目标用户",
help_text="目标用户")
target_dept = models.ManyToManyField(to=Dept, blank=True, db_constraint=False,
verbose_name="目标部门", help_text="目标部门")
target_role = models.ManyToManyField(to=Role, blank=True, db_constraint=False,
verbose_name="目标角色", help_text="目标角色")
class Meta:
db_table = table_prefix + "message_center"
verbose_name = "消息中心"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class MessageCenterTargetUser(CoreModel):
users = models.ForeignKey(Users, related_name="target_user", on_delete=models.CASCADE, db_constraint=False,
verbose_name="关联用户表", help_text="关联用户表")
messagecenter = models.ForeignKey(MessageCenter, on_delete=models.CASCADE, db_constraint=False,
verbose_name="关联消息中心表", help_text="关联消息中心表")
is_read = models.BooleanField(default=False, blank=True, null=True, verbose_name="是否已读", help_text="是否已读")
class Meta:
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",)

27
dvadmin/system/signals.py Normal file
View File

@ -0,0 +1,27 @@
import time
from django.db.models.signals import post_save, post_delete
from django.dispatch import Signal, receiver
from django.core.cache import cache
from dvadmin.system.models import MessageCenterTargetUser
# 初始化信号
pre_init_complete = Signal()
detail_init_complete = Signal()
post_init_complete = Signal()
# 租户初始化信号
pre_tenants_init_complete = Signal()
detail_tenants_init_complete = Signal()
post_tenants_init_complete = Signal()
post_tenants_all_init_complete = Signal()
# 租户创建完成信号
tenants_create_complete = Signal()
# 全局变量用于标记最后修改时间
last_db_change_time = time.time()
@receiver(post_save, sender=MessageCenterTargetUser)
@receiver(post_delete, sender=MessageCenterTargetUser)
def update_last_change_time(sender, **kwargs):
cache.set('last_db_change_time', time.time(), timeout=None) # 设置永不超时的键值对

107
dvadmin/system/tasks.py Normal file
View 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()

56
dvadmin/system/tests.py Normal file
View File

@ -0,0 +1,56 @@
from functools import wraps
from django.db.models import Func, F, OuterRef, Exists
from django.test import TestCase
import django
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "application.settings")
django.setup()
from dvadmin.system.models import Menu, RoleMenuPermission, RoleMenuButtonPermission, MenuButton
import time
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
run_time = end_time - start_time
print(f"{func.__name__} ran in {run_time:.6f} seconds")
return result
return wrapper
@timing_decorator
def getMenu():
data = []
queryset = Menu.objects.filter(status=1, is_catalog=False).values('name', 'id')
for item in queryset:
parent_list = Menu.get_all_parent(item['id'])
names = [d["name"] for d in parent_list]
completeName = "/".join(names)
isCheck = RoleMenuPermission.objects.filter(
menu__id=item['id'],
role__id=1,
).exists()
mbCheck = RoleMenuButtonPermission.objects.filter(
menu_button = OuterRef("pk"),
role__id=1,
)
btns = MenuButton.objects.filter(
menu__id=item['id'],
).annotate(isCheck=Exists(mbCheck)).values('id', 'name', 'value', 'isCheck',data_range=F('menu_button_permission__data_range'))
# print(b)
dicts = {
'name': completeName,
'id': item['id'],
'isCheck': isCheck,
'btns':btns
}
print(dicts)
data.append(dicts)
# print(data)
if __name__ == '__main__':
getMenu()

56
dvadmin/system/urls.py Normal file
View File

@ -0,0 +1,56 @@
from django.urls import path
from rest_framework import routers
from dvadmin.system.views.api_white_list import ApiWhiteListViewSet
from dvadmin.system.views.area import AreaViewSet
from dvadmin.system.views.clause import PrivacyView, TermsServiceView
from dvadmin.system.views.dept import DeptViewSet
from dvadmin.system.views.dictionary import DictionaryViewSet
from dvadmin.system.views.file_list import FileViewSet
from dvadmin.system.views.login_log import LoginLogViewSet
from dvadmin.system.views.menu import MenuViewSet
from dvadmin.system.views.menu_button import MenuButtonViewSet
from dvadmin.system.views.message_center import MessageCenterViewSet
from dvadmin.system.views.operation_log import OperationLogViewSet
from dvadmin.system.views.role import RoleViewSet
from dvadmin.system.views.role_menu import RoleMenuPermissionViewSet
from dvadmin.system.views.role_menu_button_permission import RoleMenuButtonPermissionViewSet
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)
system_url.register(r'menu_button', MenuButtonViewSet)
system_url.register(r'role', RoleViewSet)
system_url.register(r'dept', DeptViewSet)
system_url.register(r'user', UserViewSet)
system_url.register(r'operation_log', OperationLogViewSet)
system_url.register(r'dictionary', DictionaryViewSet)
system_url.register(r'area', AreaViewSet)
system_url.register(r'file', FileViewSet)
system_url.register(r'api_white_list', ApiWhiteListViewSet)
system_url.register(r'system_config', SystemConfigViewSet)
system_url.register(r'message_center', MessageCenterViewSet)
system_url.register(r'role_menu_button_permission', RoleMenuButtonPermissionViewSet)
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 = [
path('user/export/', UserViewSet.as_view({'post': 'export_data', })),
path('user/import/', UserViewSet.as_view({'get': 'import_data', 'post': 'import_data'})),
path('system_config/save_content/', SystemConfigViewSet.as_view({'put': 'save_content'})),
path('system_config/get_association_table/', SystemConfigViewSet.as_view({'get': 'get_association_table'})),
path('system_config/get_table_data/<int:pk>/', SystemConfigViewSet.as_view({'get': 'get_table_data'})),
path('system_config/get_relation_info/', SystemConfigViewSet.as_view({'get': 'get_relation_info'})),
# path('login_log/', LoginLogViewSet.as_view({'get': 'list'})),
# path('login_log/<int:pk>/', LoginLogViewSet.as_view({'get': 'retrieve'})),
# path('dept_lazy_tree/', DeptViewSet.as_view({'get': 'dept_lazy_tree'})),
path('clause/privacy.html', PrivacyView.as_view()),
path('clause/terms_service.html', TermsServiceView.as_view()),
]
urlpatterns += system_url.urls

File diff suppressed because one or more lines are too long

View File

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More