688 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			688 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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",)
 |