跳转至

数据库设计

数据库设计

初步规划参考下面的代码,ER 图可以在 https://www.mermaidchart.com/渲染

django 设计:

_# api/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser, Group as AuthGroup, Permission as AuthPermission # Import with aliases

class User(AbstractUser):
    email = models.EmailField(unique=True)
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    # Add unique related_name arguments to avoid clashes
    groups = models.ManyToManyField(
        AuthGroup, # Use the aliased Group
        verbose_name='groups',
        blank=True,
        help_text=(
            'The groups this user belongs to. A user will get all permissions '
            'granted to each of their groups.'
        ),
        related_name="api_user_set", # Changed related_name
        related_query_name="user",
    )
    user_permissions = models.ManyToManyField(
        AuthPermission, # Use the aliased Permission
        verbose_name='user permissions',
        blank=True,
        help_text='Specific permissions for this user.',
        related_name="api_user_permissions_set", # Changed related_name
        related_query_name="user",
    )

    def __str__(self):
        return self.email

class UserGroup(models.Model):
    name = models.CharField(max_length=100, unique=True)
    description = models.TextField(blank=True, null=True)
    # Django 的 Group 模型可以用于用户分组,这里我们创建一个新的 UserGroup
    # 如果文档中的 "用户组" 和 Django 的 Group 概念一致,可以考虑直接用 Django Group
    # 或者像这样创建一个新的模型,并通过 ManyToManyField 将 User 和 UserGroup 关联起来
    members = models.ManyToManyField(User, related_name='custom_user_groups', blank=True)
    # created_by = models.ForeignKey(User, related_name='created_user_groups', on_delete=models.SET_NULL, null=True) # 管理员创建

    def __str__(self):
        return self.name

class Device(models.Model):
    DEVICE_STATUS_CHOICES = [
        ('online', '在线'),
        ('offline', '离线'),
        ('error', '故障'),
    ]
    name = models.CharField(max_length=100)
    device_identifier = models.CharField(max_length=100, unique=True, help_text="设备唯一标识,例如 MAC 地址或序列号")
    ip_address = models.GenericIPAddressField(blank=True, null=True)
    port = models.PositiveIntegerField(blank=True, null=True)
    device_type = models.CharField(max_length=50, blank=True, null=True, help_text="例如:空调、冰箱、灯")
    brand = models.CharField(max_length=50, blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    status = models.CharField(max_length=20, choices=DEVICE_STATUS_CHOICES, default='offline')
    current_power_consumption = models.FloatField(blank=True, null=True)
    uptime_seconds = models.PositiveIntegerField(default=0, help_text="运行时间(秒)") # 可以考虑用 DateTimeField 记录上次启动时间
    last_heartbeat = models.DateTimeField(blank=True, null=True)
    # logs 和 usage_records 可以是 TextField 或关联到其他模型
    # logs = models.TextField(blank=True, null=True, help_text="JSON 格式的日志")
    # usage_records = models.TextField(blank=True, null=True, help_text="JSON 格式的使用记录")

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

class DeviceLog(models.Model): # 更规范的日志记录
    device = models.ForeignKey(Device, related_name='logs', on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now_add=True)
    log_message = models.TextField()

    def __str__(self):
        return f"Log for {self.device.name} at {self.timestamp}"

class DeviceUsageRecord(models.Model): # 更规范的使用记录
    device = models.ForeignKey(Device, related_name='usage_records', on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) # 哪个用户操作的
    action = models.CharField(max_length=100)
    timestamp = models.DateTimeField(auto_now_add=True)
    parameters = models.JSONField(blank=True, null=True) # 操作参数

    def __str__(self):
        return f"{self.user.email if self.user else 'System'} used {self.device.name}: {self.action}"


class DeviceGroup(models.Model):
    name = models.CharField(max_length=100, unique=True)
    description = models.TextField(blank=True, null=True)
    devices = models.ManyToManyField(Device, related_name='device_groups', blank=True)
    # created_by = models.ForeignKey(User, related_name='created_device_groups', on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return self.name

class PermissionLevel(models.TextChoices):
    NONE = 'none', '不可见'
    VISIBLE = 'visible', '可见'
    USABLE = 'usable', '可使用'
    CONFIGURABLE = 'configurable', '可配置'
    MONITORABLE = 'monitorable', '可监视'
    MANAGEABLE = 'manageable', '可管理'

# 用户对单个设备的权限
class UserDevicePermission(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='device_permissions')
    device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='user_permissions')
    # device_group = models.ForeignKey(DeviceGroup, on_delete=models.CASCADE, null=True, blank=True) # 文档中说的是对“设备”的权限,device_group 权限可以通过 UserGroupDevicePermission
    permission_level = models.CharField(max_length=20, choices=PermissionLevel.choices)

    class Meta:
        unique_together = ('user', 'device') # 一个用户对一个设备的权限是唯一的

    def __str__(self):
        return f"{self.user.email} - {self.device.name}: {self.get_permission_level_display()}"

# 用户对设备组的权限
class UserDeviceGroupPermission(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='device_group_permissions')
    device_group = models.ForeignKey(DeviceGroup, on_delete=models.CASCADE, related_name='user_permissions')
    permission_level = models.CharField(max_length=20, choices=PermissionLevel.choices)

    class Meta:
        unique_together = ('user', 'device_group') # 一个用户对一个设备组的权限是唯一的

    def __str__(self):
        return f"{self.user.email} - {self.device_group.name}: {self.get_permission_level_display()}"

# 用户组对单个设备/设备组的权限
class GroupDevicePermission(models.Model):
    user_group = models.ForeignKey(UserGroup, on_delete=models.CASCADE, related_name='group_device_permissions')
    device = models.ForeignKey(Device, on_delete=models.CASCADE, null=True, blank=True, related_name='group_permissions')
    device_group = models.ForeignKey(DeviceGroup, on_delete=models.CASCADE, null=True, blank=True, related_name='user_group_permissions')
    permission_level = models.CharField(max_length=20, choices=PermissionLevel.choices)

    class Meta:
        # 一个用户组对一个设备或一个设备组的权限是唯一的 (但不能同时指定 device 和 device_group)
        # 我们可以通过 clean 方法来校验
        unique_together = [
            ('user_group', 'device'),
            ('user_group', 'device_group'),
        ]

    def clean(self):
        from django.core.exceptions import ValidationError
        if self.device and self.device_group:
            raise ValidationError("Cannot assign permission to both a device and a device group simultaneously for a single user group permission entry.")
        if not self.device and not self.device_group:
            raise ValidationError("Must assign permission to either a device or a device group for a user group.")

    def __str__(self):
        target_name = self.device.name if self.device else self.device_group.name
        target_type = "Device" if self.device else "DeviceGroup"
__        return f"Group: {self.user_group.name} - {target_type}: {target_name}: {self.get_permission_level_display()}"_

ER 图:

**classDiagram
direction BT
class api_device {
   varchar(100) name
   varchar(100) device_identifier
   char(39) ip_address
   int unsigned port
   varchar(50) device_type
   varchar(50) brand
   longtext description
   varchar(20) status
   double current_power_consumption
   int unsigned uptime_seconds
   datetime(6) last_heartbeat
   bigint id
}
class api_devicegroup {
   varchar(100) name
   longtext description
   bigint id
}
class api_devicegroup_devices {
   bigint devicegroup_id
   bigint device_id
   bigint id
}
class api_devicelog {
   datetime(6) timestamp
   longtext log_message
   bigint device_id
   bigint id
}
class api_deviceusagerecord {
   varchar(100) action
   datetime(6) timestamp
   json parameters
   bigint device_id
   bigint user_id
   bigint id
}
class api_groupdevicepermission {
   varchar(20) permission_level
   bigint device_id
   bigint device_group_id
   bigint user_group_id
   bigint id
}
class api_user {
   varchar(128) password
   datetime(6) last_login
   tinyint(1) is_superuser
   varchar(150) username
   varchar(150) first_name
   varchar(150) last_name
   tinyint(1) is_staff
   tinyint(1) is_active
   datetime(6) date_joined
   varchar(254) email
   bigint id
}
class api_user_groups {
   bigint user_id
   int group_id
   bigint id
}
class api_user_user_permissions {
   bigint user_id
   int permission_id
   bigint id
}
class api_userdevicepermission {
   varchar(20) permission_level
   bigint device_id
   bigint user_id
   bigint id
}
class api_usergroup {
   varchar(100) name
   longtext description
   bigint id
}
class api_usergroup_members {
   bigint usergroup_id
   bigint user_id
   bigint id
}
class auth_group {
   varchar(150) name
   int id
}
class auth_group_permissions {
   int group_id
   int permission_id
   bigint id
}
class auth_permission {
   varchar(255) name
   int content_type_id
   varchar(100) codename
   int id
}
class auth_user {
   varchar(128) password
   datetime(6) last_login
   tinyint(1) is_superuser
   varchar(150) username
   varchar(150) first_name
   varchar(150) last_name
   varchar(254) email
   tinyint(1) is_staff
   tinyint(1) is_active
   datetime(6) date_joined
   int id
}
class auth_user_groups {
   int user_id
   int group_id
   bigint id
}
class auth_user_user_permissions {
   int user_id
   int permission_id
   bigint id
}
class django_admin_log {
   datetime(6) action_time
   longtext object_id
   varchar(200) object_repr
   smallint unsigned action_flag
   longtext change_message
   int content_type_id
   int user_id
   int id
}
class django_content_type {
   varchar(100) app_label
   varchar(100) model
   int id
}
class django_migrations {
   varchar(255) app
   varchar(255) name
   datetime(6) applied
   bigint id
}
class django_session {
   longtext session_data
   datetime(6) expire_date
   varchar(40) session_key
}

api_devicegroup_devices  -->  api_device : device_id:id
api_devicegroup_devices  -->  api_devicegroup : devicegroup_id:id
api_devicelog  -->  api_device : device_id:id
api_deviceusagerecord  -->  api_device : device_id:id
api_deviceusagerecord  -->  api_user : user_id:id
api_groupdevicepermission  -->  api_device : device_id:id
api_groupdevicepermission  -->  api_devicegroup : device_group_id:id
api_groupdevicepermission  -->  api_usergroup : user_group_id:id
api_user_groups  -->  api_user : user_id:id
api_user_groups  -->  auth_group : group_id:id
api_user_user_permissions  -->  api_user : user_id:id
api_user_user_permissions  -->  auth_permission : permission_id:id
api_userdevicepermission  -->  api_device : device_id:id
api_userdevicepermission  -->  api_user : user_id:id
api_usergroup_members  -->  api_user : user_id:id
api_usergroup_members  -->  api_usergroup : usergroup_id:id
auth_group_permissions  -->  auth_group : group_id:id
auth_group_permissions  -->  auth_permission : permission_id:id
auth_permission  -->  django_content_type : content_type_id:id
auth_user_groups  -->  auth_group : group_id:id
auth_user_groups  -->  auth_user : user_id:id
auth_user_user_permissions  -->  auth_permission : permission_id:id
auth_user_user_permissions  -->  auth_user : user_id:id
django_admin_log  -->  auth_user : user_id:id
****django_admin_log  -->  django_content_type : content_type_id:id**