171 lines
7.6 KiB
Python
171 lines
7.6 KiB
Python
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
|