Files
netmeeting-infra/device_management/models.py

171 lines
7.6 KiB
Python
Raw Normal View History

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='状态'
)
2026-05-19 15:39:55 +08:00
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