diff --git a/apps/em/migrations/0023_repair.py b/apps/em/migrations/0023_repair.py new file mode 100644 index 00000000..ea517f4b --- /dev/null +++ b/apps/em/migrations/0023_repair.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.12 on 2025-12-15 07:16 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('wf', '0005_workflow_cate'), + ('em', '0022_equipment_cd_req_addr'), + ] + + operations = [ + migrations.CreateModel( + name='Repair', + fields=[ + ('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')), + ('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')), + ('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')), + ('fault_description', models.TextField(verbose_name='故障描述')), + ('fault_cate', models.TextField(blank=True, null=True, verbose_name='故障类别')), + ('repair_start_time', models.DateTimeField(blank=True, null=True, verbose_name='维修开始时间')), + ('repair_duration', models.DecimalField(decimal_places=1, default=0.0, max_digits=5, verbose_name='维修工时(小时)')), + ('repair_description', models.TextField(blank=True, null=True, verbose_name='维修描述')), + ('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='repair_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')), + ('equipment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='repaire_equip', to='em.equipment', verbose_name='关联设备')), + ('repair_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='维修人')), + ('ticket', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='repair_ticket', to='wf.ticket', verbose_name='关联工单')), + ('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='repair_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/em/models.py b/apps/em/models.py index 70ae7ee7..0146a968 100644 --- a/apps/em/models.py +++ b/apps/em/models.py @@ -1,5 +1,5 @@ from django.db import models -from apps.utils.models import CommonBModel, CommonADModel +from apps.utils.models import CommonBModel, CommonADModel, CommonBDModel from apps.system.models import User # Create your models here. @@ -156,3 +156,20 @@ class EInspect(CommonADModel): inspect_time = models.DateTimeField("巡检时间") result = models.CharField(max_length=20, choices=INSPECT_RESULTS, verbose_name="巡检结果", help_text=str(INSPECT_RESULTS)) note = models.TextField("备注", null=True, blank=True) + + +class Repair(CommonADModel): + """ + TN:维修申请 + """ + equipment = models.ForeignKey(Equipment, verbose_name="关联设备", on_delete=models.CASCADE, related_name="repaire_equip") + fault_description = models.TextField("故障描述") + fault_cate = models.TextField("故障类别", null=True, blank=True) + repair_user = models.ForeignKey("system.user", verbose_name="维修人", on_delete=models.CASCADE, null=True, blank=True) + repair_start_time = models.DateTimeField("维修开始时间", null=True, blank=True) + repair_duration = models.DecimalField("维修工时(小时)", max_digits=5, decimal_places=1, default=0.0) + repair_description = models.TextField("维修描述", null=True, blank=True) + ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单', + on_delete=models.PROTECT, related_name='repair_ticket', null=True, blank=True) + + \ No newline at end of file diff --git a/apps/em/serializers.py b/apps/em/serializers.py index 657f6098..1c9a1bba 100644 --- a/apps/em/serializers.py +++ b/apps/em/serializers.py @@ -1,10 +1,11 @@ from apps.utils.serializers import CustomModelSerializer -from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate +from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate, Repair from apps.system.models import Dept from apps.utils.constants import EXCLUDE_FIELDS, EXCLUDE_FIELDS_BASE from rest_framework import serializers from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ParseError +from apps.wf.serializers import TicketSimpleSerializer class EcateSerializer(CustomModelSerializer): @@ -75,4 +76,20 @@ class EInspectSerializer(CustomModelSerializer): class CdSerializer(serializers.Serializer): - method = serializers.CharField(label="方法名") \ No newline at end of file + method = serializers.CharField(label="方法名") + + +class RepairCreateSerializer(CustomModelSerializer): + class Meta: + model = Repair + fields = ["id", "equipment", "fault_description", "fault_cate"] + +class RepairListSerializer(CustomModelSerializer): + equipment_fullname = serializers.StringRelatedField(source="equipment", read_only=True) + repair_user_name = serializers.CharField(source="repair_user.name", read_only=True) + ticket_ = TicketSimpleSerializer(source="ticket", read_only=True) + + class Meta: + model = Repair + fields = "__all__" + read_only_fields = EXCLUDE_FIELDS \ No newline at end of file diff --git a/apps/em/urls.py b/apps/em/urls.py index a5247089..d94d90de 100644 --- a/apps/em/urls.py +++ b/apps/em/urls.py @@ -1,6 +1,6 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.em.views import EquipmentViewSet, EcheckRecordViewSet, EInspectViewSet, EcateViewSet, CdView +from apps.em.views import EquipmentViewSet, EcheckRecordViewSet, EInspectViewSet, EcateViewSet, CdView, RepairViewSet API_BASE_URL = 'api/em/' HTML_BASE_URL = 'dhtml/em/' @@ -10,6 +10,7 @@ router.register('equipment', EquipmentViewSet, basename='equipment') router.register('echeckrecord', EcheckRecordViewSet, basename='echeckrecord') router.register('einspect', EInspectViewSet, basename='einspect') router.register('ecate', EcateViewSet, basename='ecate') +router.register('repair', RepairViewSet, basename='repair') urlpatterns = [ path(API_BASE_URL, include(router.urls)), path(API_BASE_URL + 'cd/', CdView.as_view()), diff --git a/apps/em/views.py b/apps/em/views.py index b39a07af..5e4d415e 100644 --- a/apps/em/views.py +++ b/apps/em/views.py @@ -2,9 +2,10 @@ from django.shortcuts import render from drf_yasg.utils import swagger_auto_schema from drf_yasg import openapi from django.utils import timezone -from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate +from apps.em.models import Equipment, EcheckRecord, EInspect, Ecate, Repair from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet -from apps.em.serializers import EquipmentSerializer, EcheckRecordSerializer, EInspectSerializer, EcateSerializer, CdSerializer +from apps.em.serializers import (EquipmentSerializer, EcheckRecordSerializer, +EInspectSerializer, EcateSerializer, CdSerializer, RepairCreateSerializer, RepairListSerializer) from apps.em.filters import EquipFilterSet from rest_framework.exceptions import ParseError from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin @@ -20,6 +21,9 @@ from apps.enp.services import get_last_envdata from rest_framework.views import APIView from apps.utils.mixins import MyLoggingMixin import importlib +from apps.wf.mixins import TicketMixin +from apps.wf.models import Ticket +from apps.system.models import User # Create your views here. @@ -188,4 +192,49 @@ class CdView(MyLoggingMixin, APIView): module, func = m.rsplit(".", 1) m = importlib.import_module(module) f = getattr(m, func) - return Response(f(*args)) \ No newline at end of file + return Response(f(*args)) + + +class RepairViewSet(TicketMixin, CustomModelViewSet): + """ + list:维修申请 + + 维修申请 + """ + queryset = Repair.objects.all() + create_serializer_class = RepairCreateSerializer + list_serializer_class = RepairListSerializer + retrieve_serializer_class = RepairListSerializer + select_related_fields = ["equipment", "repair_user", "create_by"] + filterset_fields = ["equipment", "repair_user", "ticket__state__type"] + search_fields = ["equipment__name", "equipment__number", "fault_description", "fault_cate", "repair_description"] + workflow_key = "wf_repair" + + def gen_other_ticket_data(self, instance): + return {"equipment_fullname": str(instance.equipment)} + + @staticmethod + def assgin(ticket: Ticket, transition, new_ticket_data: dict): + repair_user = new_ticket_data.get("repair_user", None) + fault_cate = new_ticket_data.get("fault_cate", None) + if repair_user and fault_cate: + repair = Repair.objects.get(ticket=ticket) + repair.repair_user = User.objects.get(id=repair_user) + repair.fault_cate = fault_cate + repair.save() + else: + raise ParseError("请分派维修人") + + @staticmethod + def repair_completed(ticket: Ticket, transition, new_ticket_data: dict): + repair_start_time = new_ticket_data.get("repair_start_time", None) + repair_duration = new_ticket_data.get("repair_duration", None) + repair_description = new_ticket_data.get("repair_description", None) + if repair_start_time and repair_duration: + repair = Repair.objects.get(ticket=ticket) + repair.repair_start_time = repair_start_time + repair.repair_duration = repair_duration + repair.repair_description = repair_description + repair.save() + else: + raise ParseError("请填写维修开始时间和维修工时") \ No newline at end of file