Initial commit: 视频主设备管理系统 - 完整DRF后端项目
This commit is contained in:
170
device_management/models.py
Normal file
170
device_management/models.py
Normal file
@@ -0,0 +1,170 @@
|
||||
import os
|
||||
from datetime import date
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from django.core.files.base import ContentFile
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class DeviceStatus(models.TextChoices):
|
||||
NORMAL = 'normal', _('正常')
|
||||
WARNING = 'warning', _('告警')
|
||||
OFFLINE = 'offline', _('离线')
|
||||
REPAIR = 'repair', _('维修中')
|
||||
SCRAP = 'scrap', _('已报废')
|
||||
|
||||
|
||||
def device_attachment_path(instance, filename):
|
||||
return f'devices/{instance.id}/attachments/{filename}'
|
||||
|
||||
|
||||
def device_thumbnail_path(instance, filename):
|
||||
return f'devices/{instance.id}/thumbnails/{filename}'
|
||||
|
||||
|
||||
class Device(models.Model):
|
||||
location = models.CharField(max_length=100, verbose_name='地点')
|
||||
building = models.CharField(max_length=50, blank=True, null=True, verbose_name='楼栋')
|
||||
floor = models.CharField(max_length=20, blank=True, null=True, verbose_name='楼层')
|
||||
cabinet = models.CharField(max_length=50, blank=True, null=True, verbose_name='机柜')
|
||||
device_name = models.CharField(max_length=100, verbose_name='设备名称')
|
||||
model = models.CharField(max_length=100, blank=True, null=True, verbose_name='型号')
|
||||
brand = models.CharField(max_length=100, blank=True, null=True, verbose_name='品牌')
|
||||
mac_address = models.CharField(max_length=17, blank=True, null=True, verbose_name='MAC地址')
|
||||
enable_date = models.DateField(blank=True, null=True, verbose_name='启用日期')
|
||||
last_inspection_date = models.DateField(blank=True, null=True, verbose_name='最后巡检日期')
|
||||
thumbnail = models.ImageField(upload_to=device_thumbnail_path, blank=True, null=True, verbose_name='缩略图')
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=DeviceStatus.choices,
|
||||
default=DeviceStatus.NORMAL,
|
||||
verbose_name='状态'
|
||||
)
|
||||
responsible_person = models.CharField(max_length=50, blank=True, null=True, verbose_name='负责人')
|
||||
warranty_expire = models.DateField(blank=True, null=True, verbose_name='保修到期日期')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'device'
|
||||
verbose_name = '设备'
|
||||
verbose_name_plural = '设备'
|
||||
indexes = [
|
||||
models.Index(fields=['location', 'building']),
|
||||
models.Index(fields=['status']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.device_name} - {self.location}'
|
||||
|
||||
@property
|
||||
def service_duration_days(self):
|
||||
if self.enable_date:
|
||||
return (date.today() - self.enable_date).days
|
||||
return 0
|
||||
|
||||
@property
|
||||
def is_warranty_expired(self):
|
||||
if self.warranty_expire:
|
||||
return date.today() > self.warranty_expire
|
||||
return False
|
||||
|
||||
def generate_thumbnail(self, image_field, size=(200, 200)):
|
||||
if not image_field:
|
||||
return
|
||||
|
||||
try:
|
||||
img = Image.open(image_field)
|
||||
if img.mode in ('RGBA', 'P'):
|
||||
img = img.convert('RGB')
|
||||
|
||||
img.thumbnail(size)
|
||||
thumb_io = BytesIO()
|
||||
img.save(thumb_io, format='JPEG', quality=85)
|
||||
|
||||
thumb_name = f'thumb_{os.path.basename(image_field.name)}'
|
||||
self.thumbnail.save(thumb_name, ContentFile(thumb_io.getvalue()), save=False)
|
||||
logger.info(f'Generated thumbnail for device {self.id}')
|
||||
except Exception as e:
|
||||
logger.error(f'Failed to generate thumbnail: {e}')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class DeviceSerial(models.Model):
|
||||
device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='serials', verbose_name='设备')
|
||||
serial_number = models.CharField(max_length=100, unique=True, verbose_name='序列号')
|
||||
serial_type = models.CharField(max_length=50, default='main', verbose_name='序列号类型')
|
||||
is_primary = models.BooleanField(default=False, verbose_name='是否主序列号')
|
||||
remark = models.CharField(max_length=255, blank=True, null=True, verbose_name='备注')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'device_serial'
|
||||
verbose_name = '设备序列号'
|
||||
verbose_name_plural = '设备序列号'
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.serial_number} ({self.serial_type})'
|
||||
|
||||
|
||||
class DeviceIP(models.Model):
|
||||
device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='ips', verbose_name='设备')
|
||||
ip_address = models.CharField(max_length=45, unique=True, verbose_name='IP地址')
|
||||
ip_type = models.CharField(max_length=20, default='management', verbose_name='IP类型')
|
||||
is_primary = models.BooleanField(default=False, verbose_name='是否主IP')
|
||||
subnet_mask = models.CharField(max_length=15, blank=True, null=True, verbose_name='子网掩码')
|
||||
gateway = models.CharField(max_length=45, blank=True, null=True, verbose_name='网关')
|
||||
vlan_id = models.CharField(max_length=10, blank=True, null=True, verbose_name='VLAN ID')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'device_ip'
|
||||
verbose_name = '设备IP地址'
|
||||
verbose_name_plural = '设备IP地址'
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.ip_address} ({self.ip_type})'
|
||||
|
||||
|
||||
class MaintenanceRecord(models.Model):
|
||||
device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='maintenance_records', verbose_name='设备')
|
||||
maintenance_date = models.DateField(verbose_name='维修日期')
|
||||
fault_description = models.TextField(blank=True, null=True, verbose_name='故障描述')
|
||||
repair_content = models.TextField(blank=True, null=True, verbose_name='维修内容')
|
||||
replaced_parts = models.CharField(max_length=255, blank=True, null=True, verbose_name='更换配件')
|
||||
maintenance_by = models.CharField(max_length=50, blank=True, null=True, verbose_name='维修人')
|
||||
cost = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name='费用')
|
||||
remark = models.TextField(blank=True, null=True, verbose_name='备注')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'maintenance_record'
|
||||
verbose_name = '维修记录'
|
||||
verbose_name_plural = '维修记录'
|
||||
ordering = ['-maintenance_date']
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.device.device_name} - {self.maintenance_date}'
|
||||
|
||||
|
||||
class DeviceAttachment(models.Model):
|
||||
device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='attachments', verbose_name='设备')
|
||||
file = models.FileField(upload_to=device_attachment_path, verbose_name='文件')
|
||||
file_name = models.CharField(max_length=255, verbose_name='文件名')
|
||||
file_type = models.CharField(max_length=50, blank=True, null=True, verbose_name='文件类型')
|
||||
uploaded_at = models.DateTimeField(auto_now_add=True, verbose_name='上传时间')
|
||||
|
||||
class Meta:
|
||||
db_table = 'device_attachment'
|
||||
verbose_name = '设备附件'
|
||||
verbose_name_plural = '设备附件'
|
||||
|
||||
def __str__(self):
|
||||
return self.file_name
|
||||
Reference in New Issue
Block a user