#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import hashlib
import logging
import os
import uuid
from pathlib import PurePosixPath

from django.conf import settings
from django.contrib.auth.models import AbstractUser, AnonymousUser
from django.db import models
from django_celery_beat.models import PeriodicTask

from django_base_ai import dispatch
from django_base_ai.system.ext_field.core_base_auth import CoreBaseAuthField
from django_base_ai.system.ext_field.models_safe import EncrypyField
from django_base_ai.utils.models import CoreModel, newuuid
table_prefix = "base_ai_"
logger = logging.getLogger(__name__)


def media_file_name(instance, filename):
    h = instance.md5sum
    basename, ext = os.path.splitext(filename)
    return PurePosixPath("files", h + ext.lower())


# 应用管理 appid
def create_agent_id():
    return f"appid_{str(uuid.uuid4().hex)}"


# 应用管理 密钥
def create_secret():
    return f"secret_{uuid.uuid1().hex + uuid.uuid4().hex}"


class Users(CoreModel, AbstractUser):
    dept = models.ForeignKey(
        to="Dept",
        verbose_name="所属部门",
        on_delete=models.PROTECT,
        db_constraint=False,
        null=True,
        blank=True,
        help_text="关联部门",
        db_index=True,
    )
    parent = models.ForeignKey(
        to="Users",
        on_delete=models.CASCADE,
        default=None,
        verbose_name="上级领导",
        db_constraint=False,
        null=True,
        blank=True,
        help_text="上级领导",
        db_index=True,
    )
    username = models.CharField(
        max_length=255,
        unique=True,
        db_index=True,
        verbose_name="用户账号",
        help_text="用户账号",
        null=False,
        blank=False,
    )
    employee_no = models.CharField(
        max_length=255, unique=True, db_index=True, null=True, blank=True, verbose_name="工号", help_text="工号"
    )
    position = models.CharField(max_length=255, verbose_name="职位", null=True, blank=True, help_text="职位")
    email = models.EmailField(
        max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱", db_index=True
    )
    mobile = EncrypyField(max_length=255, verbose_name="手机", null=True, blank=True, help_text="手机")
    landline = 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=255, verbose_name="姓名", help_text="姓名", db_index=True, null=False, blank=False, default=""
    )
    alias = models.CharField(
        max_length=255, verbose_name="成员别名", help_text="成员别名", db_index=True, null=True, blank=True
    )
    main_department = models.CharField(max_length=255, verbose_name="主部门", help_text="主部门", null=True, blank=True)
    sort = models.IntegerField(default=1, verbose_name="排序", help_text="排序")
    pwd_defaulted = models.BooleanField(default=True, 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, "借调"),
        (2, "外包"),
    )
    user_type = models.IntegerField(
        choices=USER_TYPE,
        default=1,
        verbose_name="用户类型",
        null=True,
        blank=True,
        help_text="用户类型",
        db_index=True,
    )

    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="关联角色"
    )

    last_token = models.CharField(
        max_length=255, null=True, blank=True, verbose_name="最后一次登录Token", help_text="最后一次登录Token"
    )
    skin = models.TextField(verbose_name="皮肤", choices=(), null=True, blank=True)
    node_type = models.IntegerField(
        default=4, verbose_name="节点类型", help_text="用于处理组织架构标签4表示用户", db_index=True
    )
    user_config = models.JSONField(
        max_length=50000, verbose_name="用户配置", help_text="用户配置", null=True, blank=True, default=dict
    )
    authorization_code = EncrypyField(max_length=255, verbose_name="授权码", null=True, blank=True, help_text="授权码")

    # 禁止落库的用户名，遇到则直接跳过 save，不新增也不更新
    _SKIP_SAVE_USERNAMES = ("AnonymousUser", "_ANONYMOUS_USER_BLOCKED_")

    def set_password(self, raw_password):
        super().set_password(raw_password)

    def save(self, *args, **kwargs):
        if self.username in self._SKIP_SAVE_USERNAMES:
            return
        super().save(*args, **kwargs)

    def clean(self):
        """模型验证：禁止创建用户名为 AnonymousUser 的用户"""
        from django.core.exceptions import ValidationError

        if self.username == "AnonymousUser":
            raise ValidationError("不能创建用户名为 'AnonymousUser' 的用户")
        super().clean()

    class Meta:
        db_table = table_prefix + "system_users"
        verbose_name = verbose_name_plural = "用户表"
        ordering = ("-create_datetime",)


class MySuperiors(CoreModel):
    user_info = models.ForeignKey(
        to="Users",
        db_constraint=False,
        related_name="userSuperiors",
        on_delete=models.PROTECT,
        verbose_name="协作人",
        help_text="协作人",
    )

    class Meta:
        db_table = table_prefix + "my_superiors"
        verbose_name = verbose_name_plural = "协作人"
        ordering = ("-create_datetime",)


class Post(CoreModel):
    name = models.CharField(null=False, max_length=64, verbose_name="岗位名称", help_text="岗位名称", db_index=True)
    code = models.CharField(max_length=32, verbose_name="岗位编码", help_text="岗位编码", db_index=True)
    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="岗位状态", db_index=True
    )

    class Meta:
        db_table = table_prefix + "system_post"
        verbose_name = verbose_name_plural = "岗位表"
        ordering = ("sort",)


class Role(CoreModel):
    name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称", db_index=True)
    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="角色状态", db_index=True)
    admin = models.BooleanField(default=False, verbose_name="是否为admin", help_text="是否为admin", db_index=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", verbose_name="数据权限-关联部门", db_constraint=False, help_text="数据权限-关联部门"
    )
    menu = models.ManyToManyField(to="Menu", verbose_name="关联菜单", db_constraint=False, help_text="关联菜单")
    permission = models.ManyToManyField(
        to="MenuButton", verbose_name="关联菜单的接口按钮", db_constraint=False, help_text="关联菜单的接口按钮"
    )
    node_type = models.IntegerField(default=2, verbose_name="节点类型", help_text="用于处理组织架构菜单2表示角色")
    remark = models.TextField(verbose_name="备注", help_text="备注", null=True, blank=True)

    class Meta:
        db_table = table_prefix + "system_role"
        verbose_name = verbose_name_plural = "角色表"
        ordering = ("sort",)


class Dept(CoreModel):
    parent = models.ForeignKey(
        to="Dept",
        on_delete=models.CASCADE,
        default=None,
        verbose_name="上级部门",
        db_constraint=False,
        null=True,
        blank=True,
        help_text="上级部门",
        db_index=True,
    )
    name = models.CharField(max_length=255, verbose_name="部门名称", help_text="部门名称", db_index=True)
    short_name = models.CharField(
        max_length=255, verbose_name="部门简称", help_text="部门简称", null=True, blank=True, default=""
    )
    dept_full_path = models.CharField(
        max_length=255, verbose_name="架构全路径", help_text="架构全路径", null=True, blank=True, default=""
    )
    key = models.CharField(
        max_length=255,
        unique=True,
        null=True,
        blank=True,
        verbose_name="关联字符",
        help_text="关联字符",
        default=newuuid,
    )
    sort = models.IntegerField(default=1, verbose_name="显示排序", help_text="显示排序")
    owner_user = models.ForeignKey(
        to=Users,
        related_name="owner_user",
        null=True,
        blank=True,
        verbose_name="关联用户",
        db_constraint=False,
        default=None,
        on_delete=models.CASCADE,
        help_text="关联用户",
    )
    phone = EncrypyField(max_length=255, verbose_name="座机", null=True, blank=True, help_text="座机")
    email = models.EmailField(
        max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱", db_index=True
    )
    status = models.BooleanField(
        default=True, verbose_name="部门状态", null=True, blank=True, help_text="部门状态", db_index=True
    )
    node_type = models.IntegerField(default=1, verbose_name="节点类型", help_text="用于处理组织架构菜单1表示部门")

    @classmethod
    def recursion_dept_info(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_dept_info(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 = "部门表"
        ordering = ("sort",)


# 用户自定义标签
class Tags(CoreModel):
    name = models.CharField(max_length=255, verbose_name="标签名称", help_text="标签名称", db_index=True)
    sort = models.IntegerField(default=1, verbose_name="标签顺序", help_text="标签顺序")
    status = models.BooleanField(default=True, verbose_name="标签状态", help_text="标签状态", db_index=True)
    dept = models.ManyToManyField(
        to="Dept", blank=True, verbose_name="关联部门", db_constraint=False, help_text="关联部门"
    )
    user_info = models.ManyToManyField(
        to="Users",
        blank=True,
        verbose_name="关联用户",
        db_constraint=False,
        help_text="关联用户",
        related_name="tags_user_info",
    )
    read_user_info = models.ManyToManyField(
        to="Users",
        blank=True,
        verbose_name="仅使用",
        db_constraint=False,
        help_text="仅使用",
        related_name="tags_read_user_info",
    )
    edit_user_info = models.ManyToManyField(
        to="Users",
        blank=True,
        verbose_name="读写",
        db_constraint=False,
        help_text="读写",
        related_name="tags_edit_user_info",
    )
    node_type = models.IntegerField(default=3, verbose_name="节点类型", help_text="用于处理组织架构标签3表示标签")

    remark = models.TextField(verbose_name="备注", help_text="备注", null=True, blank=True)

    class Meta:
        db_table = table_prefix + "tags"
        verbose_name = verbose_name_plural = "自定义标签"
        ordering = ("-create_datetime",)


# 组织架构基础类
class CoreBaseAuth(models.Model):
    target_dept = models.ManyToManyField(
        to=Dept, blank=True, db_constraint=False, verbose_name="部门", help_text="部门", related_name="target_dept"
    )
    target_role = models.ManyToManyField(
        to=Role, blank=True, db_constraint=False, verbose_name="角色", help_text="角色", related_name="target_role"
    )
    target_tags = models.ManyToManyField(
        to=Tags, blank=True, db_constraint=False, verbose_name="标签", help_text="标签", related_name="target_tags"
    )
    target_users = models.ManyToManyField(
        to=Users, blank=True, db_constraint=False, verbose_name="用户", help_text="用户", related_name="target_users"
    )
    target_data = models.JSONField(
        max_length=50000, verbose_name="上送json", help_text="上送json", null=True, blank=True, default=dict
    )

    class Meta:
        db_table = table_prefix + "auth"
        verbose_name = verbose_name_plural = "基础授权表"
        ordering = ("-id",)


class Menu(CoreModel):
    parent = models.ForeignKey(
        to="Menu",
        on_delete=models.CASCADE,
        verbose_name="上级菜单",
        null=True,
        blank=True,
        db_constraint=False,
        help_text="上级菜单",
    )
    name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称", db_index=True)
    path = models.CharField(
        max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址", db_index=True
    )
    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="组件名称"
    )
    redirect = models.CharField(
        max_length=128, verbose_name="重定向到子路由", null=True, blank=True, help_text="重定向到子路由"
    )
    sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
    status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态", db_index=True)
    meta = models.JSONField(max_length=5000, verbose_name="meta", help_text="meta", null=True, blank=True)

    class Meta:
        db_table = table_prefix + "system_menu"
        verbose_name = verbose_name_plural = "菜单表"
        ordering = ("sort",)


class MenuButton(CoreModel):
    menu = models.ForeignKey(
        to="Menu",
        db_constraint=False,
        related_name="menuPermission",
        on_delete=models.PROTECT,
        verbose_name="关联菜单",
        help_text="关联菜单",
    )
    name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
    value = models.CharField(max_length=64, verbose_name="权限值", help_text="权限值", db_index=True)
    api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址", db_index=True)
    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 = "菜单权限表"
        ordering = ("-name",)


class Dictionary(CoreModel):
    TYPE_LIST = (
        (0, "text"),
        (1, "number"),
        (2, "date"),
        (3, "datetime"),
        (4, "time"),
        (5, "files"),
        (6, "boolean"),
        (7, "images"),
        (8, "datetimerange"),
        (9, "tree"),
    )
    label = models.CharField(
        max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称", db_index=True
    )
    value = models.CharField(
        max_length=200, blank=True, null=True, verbose_name="字典编号", help_text="字典编号/实际值", db_index=True
    )
    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=225, blank=True, null=True, verbose_name="颜色", help_text="颜色")
    icon = models.CharField(max_length=225, 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="状态", db_index=True)
    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="备注")

    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 Meta:
        db_table = table_prefix + "system_dictionary"
        verbose_name = verbose_name_plural = "字典表"
        ordering = ("sort",)


class OperationLog(CoreModel):
    appid = models.CharField(
        max_length=255, verbose_name="应用id", null=True, blank=True, help_text="应用id", db_index=True
    )
    request_modular = models.CharField(
        max_length=64, verbose_name="请求模块", null=True, blank=True, help_text="请求模块", db_index=True
    )
    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="请求方式", db_index=True
    )
    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地址", db_index=True
    )
    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="响应状态码", db_index=True
    )
    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="响应状态", db_index=True)

    class Meta:
        db_table = table_prefix + "system_operation_log"
        verbose_name = verbose_name_plural = "操作日志"
        ordering = ("-create_datetime",)


class FolderList(CoreModel):
    name = models.CharField(
        max_length=200, null=True, blank=True, verbose_name="文件夹名称", help_text="文件夹名称", db_index=True
    )
    size = models.BigIntegerField(default=0, blank=True, verbose_name="文件夹大小", help_text="文件夹大小")

    class Meta:
        db_table = table_prefix + "system_folder_list"
        verbose_name = verbose_name_plural = "文件夹管理"
        ordering = ("-create_datetime",)
        unique_together = ("name",)


class FileList(CoreModel):
    folder = models.ForeignKey(
        to="FolderList",
        verbose_name="所属文件夹",
        on_delete=models.CASCADE,
        db_constraint=False,
        null=True,
        blank=True,
        help_text="所属文件夹",
    )
    name = models.CharField(max_length=200, null=True, blank=True, verbose_name="名称", help_text="名称", db_index=True)
    url = models.FileField(
        upload_to=media_file_name, null=True, blank=True, verbose_name="文件http地址", help_text="文件http地址"
    )
    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.BigIntegerField(default=0, blank=True, verbose_name="文件大小", help_text="文件大小")
    md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5", db_index=True)

    @classmethod
    def save_file(cls, request, file_path, file_name, mime_type):
        # 保存到File model中
        instance = FileList()
        instance.name = file_name
        instance.engine = dispatch.get_system_config_values("file_storage.file_engine") or "local"
        instance.file_url = os.path.join(file_path, file_name)
        instance.mime_type = mime_type
        # 检查用户是否是匿名用户，避免创建匿名用户记录
        if request.user and not isinstance(request.user, AnonymousUser):
            instance.creator = request.user.id
            instance.modifier = request.user.id
            instance.dept_belong_id = request.user.dept_id

        file_backup = dispatch.get_system_config_values("file_storage.file_backup")
        dispatch.get_system_config_values("file_storage.file_engine") or "local"
        if file_backup:
            instance.url = os.path.join(file_path.replace(f"{settings.MEDIA_ROOT}/", ""), file_name)
        instance.url = os.path.join(file_path.replace(f"{settings.MEDIA_ROOT}/", ""), file_name)
        instance.save()
        return instance

    def save(self, *args, **kwargs):
        if not self.md5sum and self.url:  # file is new
            md5 = hashlib.md5()
            for chunk in self.url.chunks():
                md5.update(chunk)
            self.md5sum = md5.hexdigest()
        if not self.size and self.url:
            self.size = self.url.size
        if not self.file_url:
            url = media_file_name(self, self.name)
            self.file_url = f"/{settings.MEDIA_ROOT}/{url}"
        super().save(*args, **kwargs)
        # 解决相同文件名称_*的问题
        if self.url:
            rel_path = (self.url.name or "").strip().replace("\\", "/")
            if rel_path:
                media_url = (getattr(settings, "MEDIA_URL", "") or "/media/").strip().rstrip("/")
                self.file_url = f"/{media_url}/{rel_path}".replace("//", "/")
                FileList.objects.filter(pk=self.pk).update(file_url=self.file_url)
    class Meta:
        db_table = table_prefix + "system_file_list"
        verbose_name = verbose_name_plural = "文件管理"
        ordering = ("-create_datetime",)


class ChunkUploadSession(CoreModel):
    """
    断点续传：切片上传会话，记录 upload_id 与已上传的切片序号，供前端查询续传。
    """

    upload_id = models.CharField(
        max_length=64, unique=True, db_index=True, verbose_name="上传会话ID", help_text="上传会话ID"
    )
    file_md5 = models.CharField(max_length=32, db_index=True, verbose_name="文件MD5", help_text="文件MD5")
    filename = models.CharField(max_length=255, blank=True, verbose_name="文件名", help_text="文件名")
    total_size = models.BigIntegerField(default=0, verbose_name="文件总大小", help_text="文件总大小")
    chunk_total = models.IntegerField(default=0, verbose_name="切片总数", help_text="切片总数")
    uploaded_chunk_indices = models.JSONField(
        default=list,
        verbose_name="已上传切片序号",
        help_text="已上传的 chunk_index 列表，如 [0,1,2]",
    )

    class Meta:
        db_table = table_prefix + "system_chunk_upload_session"
        verbose_name = verbose_name_plural = "切片上传会话"
        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.PROTECT,
        db_constraint=False,
        null=True,
        blank=True,
        help_text="父地区编码",
    )

    class Meta:
        db_table = table_prefix + "system_area"
        verbose_name = verbose_name_plural = "地区表"
        ordering = ("code",)

    def __str__(self):
        return f"{self.name}"


class ApiWhiteList(CoreModel):
    url = models.CharField(max_length=200, help_text="url地址", verbose_name="url", db_index=True)
    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 = "接口白名单"
        ordering = ("-create_datetime",)


class SystemConfig(CoreModel):
    parent = models.ForeignKey(
        to="self",
        verbose_name="父级",
        on_delete=models.PROTECT,
        db_constraint=False,
        null=True,
        blank=True,
        help_text="父级",
    )
    title = models.CharField(max_length=50, verbose_name="标题", help_text="标题", db_index=True)
    key = models.CharField(max_length=200, verbose_name="键", help_text="键", db_index=True)
    value = models.JSONField(max_length=500, 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="启用状态", db_index=True)
    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"),
        (16, "code"),
        (17, "role"),
        (18, "request"),
        (19, "response"),
    )
    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=100, null=True, blank=True, verbose_name="提示信息", help_text="提示信息")
    setting = models.JSONField(null=True, blank=True, verbose_name="配置", help_text="配置")

    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 Meta:
        db_table = table_prefix + "system_config"
        verbose_name = verbose_name_plural = "系统配置表"
        ordering = ("sort",)
        unique_together = (("key", "parent_id"),)

    def __str__(self):
        return f"{self.title}"


class LoginLog(CoreModel):
    LOGIN_TYPE_CHOICES = ((1, "默认登录"), (2, "SSO登录"), (3, "移动端"), (4, "授群码登录"), (900, "自定义登录"))
    username = models.CharField(
        max_length=150, verbose_name="登录用户名", null=True, blank=True, help_text="登录用户名", db_index=True
    )
    ip = models.CharField(
        max_length=32, verbose_name="登录ip", null=True, blank=True, help_text="登录ip", db_index=True
    )
    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="登录类型", db_index=True
    )

    class Meta:
        db_table = table_prefix + "system_login_log"
        verbose_name = verbose_name_plural = "登录日志"
        ordering = ("-create_datetime",)


class Apps(CoreModel):
    agent_id = models.CharField(
        max_length=255, verbose_name="应用编号", null=False, blank=False, help_text="应用编号", default=create_agent_id
    )
    secret = EncrypyField(
        max_length=255, verbose_name="应用密钥", null=False, blank=False, help_text="应用密钥", default=create_secret
    )
    title = models.CharField(
        verbose_name="应用名称",
        max_length=255,
        default="",
        null=False,
        blank=False,
        help_text="应用名称",
        db_index=True,
    )
    icon = models.CharField(
        verbose_name="应用图标", max_length=255, default="", null=False, blank=False, help_text="应用图标"
    )
    old_title = models.CharField(
        verbose_name="曾用名", max_length=255, default="", null=True, blank=True, help_text="曾用名"
    )
    sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
    apps_type = models.IntegerField(
        choices=dispatch.get_dictionary_choices("apps_type"),
        verbose_name="应用类型",
        default=0,
        null=True,
        blank=True,
        help_text="应用类型",
    )
    open_type = models.IntegerField(
        choices=((0, "新打开浏览器页面"), (1, "弹框显示")),
        verbose_name="应用类型",
        default=0,
        null=True,
        blank=True,
        help_text="应用类型",
    )
    visible_range = CoreBaseAuthField(
        verbose_name="可见范围", null=True, blank=True, default=dict, help_text="可见范围"
    )
    server_ip = models.TextField(
        verbose_name="服务器IP白名单", help_text="服务器IP白名单", null=True, blank=True, default=""
    )
    healthy = models.CharField(
        verbose_name="健康检查", max_length=255, default="/healthy/", null=True, blank=True, help_text="健康检查"
    )
    readiness = models.CharField(
        verbose_name="存活中间件", max_length=255, default="/readiness/", null=True, blank=True, help_text="存活中间件"
    )
    apps_zip = models.CharField(
        verbose_name="项目文档", max_length=255, default="", null=True, blank=True, help_text="项目文档"
    )
    create_or_update_callback_url = models.TextField(
        verbose_name="新增修改", help_text="新增/修改/回调地址", null=True, blank=True, default=""
    )
    delete_callback_url = models.TextField(
        verbose_name="删除用户", help_text="删除回调地址", null=True, blank=True, default=""
    )
    desc = models.TextField(verbose_name="描述", default="", null=True, blank=True, help_text="描述")

    # 自动创建第三方api用户 不行 权限如何控制

    class Meta:
        db_table = table_prefix + "apps"
        verbose_name = verbose_name_plural = "应用管理"
        ordering = ("-id",)
        unique_together = ("title",)

    def __str__(self):
        return self.title


class SendMessageTask(CoreModel):
    apps = models.ForeignKey(
        Apps, verbose_name="开放应用", help_text="开放应用", null=True, blank=True, on_delete=models.CASCADE
    )
    send_type = models.IntegerField(
        verbose_name="发送类型",
        null=True,
        blank=True,
        help_text="发送类型",
        default=0,
        choices=((0, "邮件"), (1, "客户端通知")),
    )
    task_name = models.CharField(
        verbose_name="任务名称", max_length=255, help_text="任务名称", blank=True, null=True, default=""
    )
    task_id = models.CharField(
        verbose_name="任务id", max_length=255, help_text="任务id", blank=True, null=True, default=""
    )

    class Meta:
        db_table = table_prefix + "send_message_task"
        verbose_name = verbose_name_plural = "发送任务"
        ordering = ("-id",)

    def __str__(self):
        return self.task_id


class MessageCenter(CoreModel):
    send_message_task = models.ForeignKey(
        SendMessageTask, verbose_name="开放应用", help_text="开放应用", null=True, blank=True, on_delete=models.CASCADE
    )
    title = models.CharField(max_length=100, verbose_name="标题", help_text="标题")
    short_content = models.TextField(verbose_name="简要描述", help_text="简要描述", blank=False, null=False, default="")
    thumbnail = models.TextField(verbose_name="缩略图", help_text="缩略图", blank=True, null=True, default="")
    message_type = models.IntegerField(
        default=0, choices=dispatch.get_dictionary_choices("message_type"), db_index=True
    )
    file_list = models.JSONField(verbose_name="邮件附件", default=dict, null=True, blank=True, help_text="邮件附件")
    content = models.TextField(verbose_name="内容", help_text="内容", blank=True, null=True, default="")
    url = models.TextField(verbose_name="跳转地址", help_text="跳转地址", blank=True, null=True, default="")
    target_type = models.IntegerField(
        default=0,
        choices=dispatch.get_dictionary_choices("target_type"),
        verbose_name="目标类型",
        help_text="目标类型",
        db_index=True,
    )
    is_withdraw = models.BooleanField(
        default=False, blank=True, null=True, verbose_name="是否撤回", help_text="是否撤回", db_index=True
    )
    task_id = models.CharField(
        verbose_name="任务id", max_length=255, help_text="任务id", blank=True, null=True, default=""
    )

    send_user_info = CoreBaseAuthField(
        verbose_name="发送范围", null=True, blank=True, default=dict, help_text="发送范围"
    )
    # 定时发送
    periodic_task = models.ManyToManyField(
        PeriodicTask,
        verbose_name="定时发送",
        help_text="定时发送",
        blank=True,
        related_name="many_periodic_send_task",
        db_constraint=False,
    )
    expires = models.DateTimeField(blank=True, null=True, verbose_name="过期时间", help_text="过期时间")
    ext_kwargs = models.JSONField(
        verbose_name="发送消息扩展参数", null=False, blank=False, default=dict, help_text="发送消息扩展参数"
    )

    class Meta:
        db_table = table_prefix + "message_center"
        verbose_name = verbose_name_plural = "消息中心"
        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_send = models.BooleanField(
        default=False, blank=True, null=True, verbose_name="是否推送", help_text="是否推送", db_index=True
    )
    is_read = models.BooleanField(
        default=False, blank=True, null=True, verbose_name="是否已读", help_text="是否已读", db_index=True
    )
    send_mails_result = models.CharField(
        verbose_name="发送结果", max_length=255, help_text="发送结果仅用于异步通知", blank=True, null=True, default=""
    )

    class Meta:
        db_table = table_prefix + "message_center_target_user"
        verbose_name = verbose_name_plural = "消息中心目标用户表"


# 任务管理基础版
class BaseTask(CoreModel):
    code = models.CharField(
        verbose_name="任务编号",
        max_length=255,
        default=newuuid,
        null=True,
        blank=True,
        help_text="任务编号",
        db_index=True,
    )
    task_name = models.CharField(
        verbose_name="任务名称",
        max_length=255,
        default="",
        null=False,
        blank=False,
        help_text="任务名称",
        db_index=True,
    )
    task_type = models.IntegerField(
        choices=dispatch.get_dictionary_choices("task_type"),
        verbose_name="任务类型",
        default=0,
        null=False,
        blank=False,
        help_text="任务类型",
        db_index=True,
    )
    periodic_task = models.ManyToManyField(
        PeriodicTask,
        verbose_name="周期性任务",
        help_text="周期性任务",
        blank=True,
        related_name="base_task_periodic_task",
    )
    task_status = models.BooleanField(default=False, verbose_name="任务状态", help_text="任务状态", db_index=True)
    request_config = models.JSONField(
        verbose_name="请求配置", max_length=50000, default=dict, null=True, blank=True, help_text="请求配置"
    )
    last_run_time = models.DateTimeField(
        blank=True, null=True, verbose_name="最后一次运行时间", help_text="最后一次运行时间"
    )
    task_desc = models.TextField(verbose_name="任务描述", default="", null=True, blank=True, help_text="任务描述")

    class Meta:
        db_table = table_prefix + "base_task"
        verbose_name = verbose_name_plural = "任务管理"

        ordering = ("-id",)
        # unique_together = ('task_name',)

    def __str__(self):
        return self.task_name


class TaskRunHistory(CoreModel):
    base_task = models.ForeignKey(
        BaseTask,
        on_delete=models.CASCADE,
        db_constraint=False,
        null=True,
        blank=True,
        verbose_name="触发任务",
        help_text="触发任务",
    )
    url_name = models.CharField(
        verbose_name="url命名", max_length=255, default="", null=False, blank=False, help_text="url命名"
    )
    start_time = models.DateTimeField(
        blank=True, null=True, verbose_name="任务请求开始时间", help_text="请求开始时间", db_index=True
    )
    end_time = models.DateTimeField(blank=True, null=True, verbose_name="任务请求结束时间", help_text="请求结束时间")
    status_code = models.IntegerField(
        verbose_name="请求状态响应码", default=0, help_text="请求状态响应码", db_index=True
    )
    request_config = models.JSONField(
        verbose_name="请求配置", max_length=50000, default=dict, null=True, blank=True, help_text="请求配置"
    )
    run_result = models.TextField(
        verbose_name="运行返回结果", default=dict, null=True, blank=True, help_text="运行返回结果"
    )
    periodic_task = models.ForeignKey(
        to=PeriodicTask,
        on_delete=models.SET_NULL,
        db_constraint=False,
        related_name="periodic",
        verbose_name="执行任务定时器",
        help_text="执行任务定时器",
        null=True,
        blank=True,
    )

    class Meta:
        db_table = table_prefix + "task_run_history"
        verbose_name = verbose_name_plural = "任务运行历史"
        ordering = ("-id",)


# 第三方服务依赖
class ExternalRely(CoreModel):
    apps = models.ForeignKey(
        Apps,
        verbose_name="开放应用",
        help_text="开放应用",
        null=True,
        blank=True,
        db_constraint=False,
        on_delete=models.CASCADE,
    )
    title = models.CharField(
        verbose_name="应用名称", max_length=255, default="", null=True, blank=True, help_text="应用名称"
    )
    ip = models.CharField(
        verbose_name="服务域名", max_length=255, default="", null=False, blank=False, help_text="服务域名"
    )
    port = models.IntegerField(verbose_name="端口", default=80, null=False, blank=False, help_text="端口")
    url = models.TextField(
        verbose_name="url地址", max_length=50000, default="", null=False, blank=False, help_text="url地址"
    )

    class Meta:
        db_table = table_prefix + "external_rely"
        verbose_name = verbose_name_plural = "第三方服务依赖"
        ordering = ("-id",)

    def __str__(self):
        return self.title


# 埋点
# WHO（什么人），When（在什么时间），Where（在什么地点），How（以什么方式），What（干了什么事情）
class AppsBuriedPoint(CoreModel):
    apps = models.ForeignKey(
        Apps,
        verbose_name="开放应用",
        help_text="开放应用",
        null=True,
        blank=True,
        db_constraint=False,
        on_delete=models.CASCADE,
    )


class AIDeskAppManage(CoreModel):
    agent_id = models.CharField(
        max_length=255, verbose_name="应用编号", null=False, blank=False, help_text="应用编号", default=create_agent_id
    )
    secret = EncrypyField(
        max_length=255, verbose_name="应用密钥", null=False, blank=False, help_text="应用密钥", default=create_secret
    )
    dept = models.ForeignKey(
        to="Dept",
        verbose_name="所属部门",
        on_delete=models.PROTECT,
        db_constraint=False,
        null=True,
        blank=True,
        help_text="所属部门",
    )
    avatar = models.CharField(max_length=255, verbose_name="头像", null=True, blank=True, help_text="头像")
    name = models.CharField(
        verbose_name="名称", max_length=255, default="", null=False, blank=False, help_text="名称", db_index=True
    )
    alias = models.CharField(max_length=255, verbose_name="别名", help_text="别名", null=True, blank=True)
    email = models.EmailField(
        max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱", db_index=True
    )
    mobile = EncrypyField(max_length=255, verbose_name="手机", null=True, blank=True, help_text="手机")
    landline = models.CharField(max_length=255, verbose_name="座机", null=True, blank=True, help_text="固定电话")
    welcome_message = models.TextField(verbose_name="欢迎语", default="", null=True, blank=True, help_text="欢迎语")
    sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
    allocation_type = models.IntegerField(
        verbose_name="分配方式",
        null=True,
        blank=True,
        help_text="分配方式",
        default=0,
        choices=dispatch.get_dictionary_choices("im_chat_app_manage_stype_choices"),
    )
    is_release = models.BooleanField(
        verbose_name="是否发布", default=False, null=True, blank=True, help_text="是否发布", db_index=True
    )
    visibility = CoreBaseAuthField(verbose_name="可见范围", null=True, blank=True, default=dict, help_text="可见范围")
    manager = CoreBaseAuthField(verbose_name="管理者", null=True, blank=True, default=dict, help_text="管理者")
    seats = CoreBaseAuthField(verbose_name="坐席", null=True, blank=True, default=dict, help_text="坐席")
    ai_switch = models.BooleanField(
        verbose_name="开启AI", default=False, null=True, blank=True, help_text="开启AI 默认False 不开启"
    )
    recommended_questions = models.BooleanField(
        verbose_name="推荐问题", default=False, null=True, blank=True, help_text="动态推荐用户可能会问的相似问题"
    )
    dissatisfied_switch = models.BooleanField(
        verbose_name="询问不满意原因", default=True, null=True, blank=True, help_text="开启满意度调查 默认True 开启"
    )
    dissatisfied = models.JSONField(
        verbose_name="不满意Json", default=dict, null=True, blank=True, help_text="不满意Json"
    )
    ai_settings_json = models.JSONField(
        verbose_name="API请求Json", default=dict, null=True, blank=True, help_text="API请求Json"
    )
    human_services = models.BooleanField(
        verbose_name="人工服务",
        default=False,
        null=True,
        blank=True,
        help_text="客户咨询时将先根据录入的知识库自动回复客户，无法回复时再转到人工服务",
    )
    desc = models.TextField(verbose_name="描述", default="", null=True, blank=True, help_text="描述")

    class Meta:
        db_table = table_prefix + "ai_desk_app_manage"
        verbose_name = verbose_name_plural = "员工服务"
        ordering = ("-id",)
        unique_together = ("name",)

    def __str__(self):
        return self.name


class IMChatQA(CoreModel):
    ai_desk_app_manage = models.ForeignKey(
        AIDeskAppManage,
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="员工服务",
        help_text="员工服务",
    )
    question = models.TextField(verbose_name="答案内容", help_text="答案内容", blank=False, null=False, default="")
    answer = models.TextField(verbose_name="答案内容", help_text="答案内容", blank=False, null=False, default="")
    q_type = models.IntegerField(
        verbose_name="类型",
        choices=((0, "热门问题"), (1, "AI"), (2, "未知")),
        null=True,
        blank=True,
        help_text="类型",
        default=0,
        db_index=True,
    )
    is_it_solved = models.IntegerField(
        verbose_name="是否解决",
        choices=((0, "解决"), (1, "未解决"), (2, "未选择")),
        help_text="是否解决",
        blank=True,
        null=True,
        default=2,
        db_index=True,
    )

    class Meta:
        db_table = table_prefix + "im_chat_qa"
        verbose_name = verbose_name_plural = "QA问答记录"


class IMChatQuestionGroup(CoreModel):
    ai_desk_app_manage = models.ForeignKey(
        AIDeskAppManage,
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="员工服务",
        help_text="员工服务",
    )
    parent = models.ForeignKey(
        to="IMChatQuestionGroup",
        on_delete=models.CASCADE,
        verbose_name="上级",
        null=True,
        blank=True,
        db_constraint=False,
        help_text="上级",
    )
    title = models.CharField(
        max_length=255,
        verbose_name="分组名称",
        help_text="分组名称",
        null=False,
        blank=False,
        default="",
        db_index=True,
    )
    sort = models.IntegerField(default=1, verbose_name="排序", help_text="排序")

    class Meta:
        db_table = table_prefix + "im_chat_question_group"
        verbose_name = verbose_name_plural = "员工服务热门问题分组"


class IMChatQuestion(CoreModel):
    im_chat_question_group = models.ForeignKey(
        IMChatQuestionGroup,
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="热门问题分组",
        help_text="热门问题分组",
    )
    parent = models.ForeignKey(
        to="IMChatQuestion",
        on_delete=models.CASCADE,
        verbose_name="上级",
        null=True,
        blank=True,
        db_constraint=False,
        help_text="上级",
    )
    title = models.CharField(
        max_length=255,
        verbose_name="问题标题",
        help_text="问题标题",
        null=False,
        blank=False,
        default="",
        db_index=True,
    )
    content = models.TextField(verbose_name="答案内容", help_text="答案内容", blank=False, null=False, default="")
    is_enable = models.BooleanField(
        verbose_name="是否启用", help_text="是否启用", blank=True, null=True, default=True, db_index=True
    )
    hit = models.IntegerField(verbose_name="点击量", help_text="点击量", blank=True, null=True, default=0)

    class Meta:
        db_table = table_prefix + "im_chat_question"
        verbose_name = verbose_name_plural = "员工服务热门问题"
        ordering = (
            "-hit",
            "id",
        )


class Collaborator(CoreModel):
    user_info = CoreBaseAuthField(verbose_name="协作人", null=True, blank=True, default=dict, help_text="协作人")

    class Meta:
        db_table = table_prefix + "collaborator"
        verbose_name = verbose_name_plural = "协作人"

        ordering = ("-create_datetime",)


class IMSession(CoreModel):
    parent = models.ForeignKey(
        to="IMSession",
        on_delete=models.SET_NULL,
        default=None,
        verbose_name="父级会话ID",
        db_constraint=False,
        null=True,
        blank=True,
        help_text="父级会话ID",
        db_index=True,
    )
    session_id = models.CharField(
        max_length=255, unique=True, null=True, blank=True, verbose_name="会话id", help_text="会话id", default=newuuid
    )
    from_user = models.ForeignKey(
        to="Users",
        db_constraint=False,
        on_delete=models.CASCADE,
        verbose_name="会话用户",
        help_text="会话用户",
        null=True,
    )
    session_type = models.BooleanField(
        default=False, verbose_name="会话类型", help_text="会话类型，0 点对点 1群聊", db_index=True
    )
    is_it_pinned = models.BooleanField(
        default=False, verbose_name="是否置顶", help_text="是否置顶，True置顶 False不置顶", db_index=True
    )
    is_claim = models.IntegerField(
        verbose_name="是否认领",
        choices=((0, "未认领"), (1, "已认领"), (2, "消息免打扰")),
        help_text="是否认领",
        null=True,
        blank=True,
        default=0,
        db_index=True,
    )
    is_colse = models.BooleanField(
        verbose_name="是否关闭", help_text="是否关闭", null=True, blank=True, default=False, db_index=True
    )
    ai_desk_app_manage = models.ForeignKey(
        AIDeskAppManage,
        null=True,
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="员工服务",
        help_text="员工服务",
    )

    class Meta:
        db_table = table_prefix + "im_session"
        verbose_name = verbose_name_plural = "会话表"
        ordering = ("-id", "-is_it_pinned")

    def __str__(self):
        return self.session_id


class IMChatRecordForm(CoreModel):
    im_session = models.OneToOneField(
        IMSession,
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="会话",
        help_text="会话",
    )
    first_reply_time = models.DateTimeField(
        verbose_name="首次回复时间", null=True, blank=True, help_text="首次回复时间"
    )
    reply_interval = models.IntegerField(
        verbose_name="回复间隔", null=True, blank=True, help_text="回复间隔", default=0
    )
    satisfaction_type = models.IntegerField(
        choices=dispatch.get_dictionary_choices("satisfaction_type"),
        verbose_name="满意度",
        null=True,
        blank=True,
        help_text="满意度",
        default=5,
    )
    satisfaction_info = models.TextField(
        verbose_name="反馈内容", help_text="反馈内容", blank=False, null=False, default=""
    )

    class Meta:
        db_table = table_prefix + "im_chat_record_form"
        verbose_name = verbose_name_plural = "满意度"
        ordering = ("id",)


# 点对点
class IMChatToUser(CoreModel):
    im_session = models.ForeignKey(
        to="IMSession",
        db_constraint=False,
        related_name="SessionFromUser",
        on_delete=models.CASCADE,
        verbose_name="会话",
        help_text="会话",
    )
    from_user = models.ForeignKey(
        to="Users",
        db_constraint=False,
        related_name="FromUser",
        on_delete=models.CASCADE,
        verbose_name="发送用户",
        help_text="发送用户",
    )
    from_to_user = models.ForeignKey(
        to="Users",
        db_constraint=False,
        related_name="FromToUser",
        on_delete=models.PROTECT,
        verbose_name="接收用户",
        help_text="接收用户",
    )
    msg_stype = models.IntegerField(
        verbose_name="消息类型",
        help_text="消息类型",
        null=True,
        choices=dispatch.get_dictionary_choices("im_stype_choices"),
        blank=True,
        default=0,
        db_index=True,
    )
    content = models.JSONField(verbose_name="内容", help_text="内容", blank=True, null=True, default=dict)

    class Meta:
        db_table = table_prefix + "im_chat_to_user"
        verbose_name = verbose_name_plural = "点对点聊天"
        ordering = ("id",)


# 群聊
class IMChatToGroup(CoreModel):
    im_session = models.ForeignKey(
        IMSession,
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="会话",
        help_text="会话",
    )
    name = models.CharField(
        max_length=255, null=True, blank=True, verbose_name="群聊名称", help_text="群聊名称", db_index=True
    )
    # 群聊管理员
    group_admins = models.ManyToManyField(
        Users, db_constraint=False, related_name="ImGroupAdmins", verbose_name="群管理员", help_text="群管理员"
    )
    group_chat_avatar = models.CharField(
        max_length=255, default="", null=True, blank=True, verbose_name="群聊头像", help_text="群聊头像"
    )
    group_ext_info = models.JSONField(
        verbose_name="扩展字段", help_text="扩展字段", blank=True, null=True, default=dict
    )
    # 群成员
    from_many_to_user = models.ManyToManyField(
        Users,
        db_constraint=False,
        related_name="ManyGroupMembers",
        verbose_name="接收用户",
        help_text="接收用户",
    )

    class Meta:
        db_table = table_prefix + "im_chat_to_group"
        verbose_name = verbose_name_plural = "群聊天"
        ordering = ("-id",)


class IMChatToGroupMessage(CoreModel):
    im_chat_to_group = models.ForeignKey(
        to="IMChatToGroup",
        on_delete=models.CASCADE,
        db_constraint=False,
        verbose_name="群聊天",
        help_text="群聊天",
    )
    from_user = models.ForeignKey(
        to="Users",
        db_constraint=False,
        related_name="GroupFromUser",
        on_delete=models.CASCADE,
        verbose_name="发送用户",
        help_text="发送用户",
    )
    from_to_user = models.ForeignKey(
        to="Users",
        db_constraint=False,
        related_name="GroupFromToUser",
        on_delete=models.PROTECT,
        verbose_name="接收用户",
        help_text="接收用户",
    )
    content = models.JSONField(verbose_name="内容", help_text="内容", blank=True, null=True, default=dict)
    msg_stype = models.IntegerField(
        verbose_name="消息类型",
        help_text="消息类型",
        null=True,
        choices=dispatch.get_dictionary_choices("im_stype_choices"),
        blank=True,
        default=0,
        db_index=True,
    )
    is_read = models.BooleanField(
        default=True, blank=True, null=True, verbose_name="是否已读", help_text="是否已读", db_index=True
    )

    class Meta:
        db_table = table_prefix + "im_chat_to_group_message"
        verbose_name = verbose_name_plural = "群聊天内容"
        ordering = ("-id",)


class SafetyCheckRecord(CoreModel):
    pkg_count = models.IntegerField(verbose_name="扫描包数", help_text="扫描包数", blank=True, null=True, default=0)
    vulnerabilities_count = models.IntegerField(
        verbose_name="漏洞包数量", help_text="漏洞包数量", blank=True, null=True, default=0
    )
    vulnerabilities = models.JSONField(
        max_length=50000, verbose_name="漏洞详情", help_text="漏洞详情", null=True, blank=True, default=dict
    )

    class Meta:
        db_table = table_prefix + "safety_check_record"
        verbose_name = verbose_name_plural = "安全漏洞检查记录"
        ordering = ("-id",)


class FrequentlyUsedContacts(CoreModel):
    user = models.ForeignKey(
        to="Users",
        on_delete=models.CASCADE,
        default=None,
        verbose_name="常用联系人",
        db_constraint=False,
        null=True,
        blank=True,
        help_text="常用联系人",
        db_index=True,
    )

    class Meta:
        db_table = table_prefix + "frequently_used_contacts"
        verbose_name = verbose_name_plural = "常用联系人"
        ordering = ("-id",)
        # unique_together = ('creator', 'user')


class SystemUsage(CoreModel):
    apps = models.ForeignKey(
        Apps,
        verbose_name="开放应用",
        help_text="开放应用",
        null=True,
        blank=True,
        db_constraint=False,
        on_delete=models.CASCADE,
    )
    use_count = models.IntegerField(default=0, verbose_name="使用总数", help_text="使用总数")
    usage_type = models.IntegerField(default=0, verbose_name="类型", help_text="类型", db_index=True)
    title = models.TextField(verbose_name="模块名称", help_text="模块名称", blank=False, null=False, default="")
    path = models.TextField(verbose_name="路径", help_text="路径", blank=False, null=False, default="")

    class Meta:
        db_table = table_prefix + "system_usage"
        verbose_name = verbose_name_plural = "统计量"
        ordering = ("-id",)

