From 5cdc4c3c286e7816c9cda0ecff3ac960b24cb585 Mon Sep 17 00:00:00 2001 From: xinwen Date: Mon, 10 Jan 2022 20:14:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B7=A5=E5=8D=95=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=BA=8F=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0013_ticket_serial_num.py | 44 ++++++++++++++++ apps/tickets/models/ticket.py | 51 +++++++++++++++++++ apps/tickets/serializers/ticket/ticket.py | 3 +- apps/tickets/signals_handler/ticket.py | 11 ++-- 4 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 apps/tickets/migrations/0013_ticket_serial_num.py diff --git a/apps/tickets/migrations/0013_ticket_serial_num.py b/apps/tickets/migrations/0013_ticket_serial_num.py new file mode 100644 index 000000000..3da9f3b8b --- /dev/null +++ b/apps/tickets/migrations/0013_ticket_serial_num.py @@ -0,0 +1,44 @@ +# Generated by Django 3.1.13 on 2022-01-10 07:37 + +from django.db import migrations, models + +from common.utils.timezone import as_current_tz + + +def fill_ticket_serial_number(apps, schema_editor): + Ticket = apps.get_model('tickets', 'Ticket') + tickets = Ticket.objects.all().order_by('date_created') + + curr_day = '00000000' + curr_num = 1 + + print(f'\nFill ticket serial number ... ', end='') + for ticket in tickets: + # 跑这个脚本的时候,所有 ticket.serial_num == null + date_created = as_current_tz(ticket.date_created) + date_str = date_created.strftime('%Y%m%d') + if date_str != curr_day: + curr_day = date_str + curr_num = 1 + + ticket.serial_num = curr_day + '%04d' % curr_num + curr_num += 1 + + Ticket.objects.bulk_update(tickets, fields=('serial_num',)) + print(len(tickets), end='') + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0012_ticketsession'), + ] + + operations = [ + migrations.AddField( + model_name='ticket', + name='serial_num', + field=models.CharField(max_length=256, null=True, unique=True, verbose_name='Serial number'), + ), + migrations.RunPython(fill_ticket_serial_number), + ] diff --git a/apps/tickets/models/ticket.py b/apps/tickets/models/ticket.py index 0ac415f22..8047c4a06 100644 --- a/apps/tickets/models/ticket.py +++ b/apps/tickets/models/ticket.py @@ -3,7 +3,11 @@ from django.db import models from django.db.models import Q from django.utils.translation import ugettext_lazy as _ +from datetime import timedelta +from django.db.utils import IntegrityError +from common.exceptions import JMSException +from common.utils.timezone import as_current_tz from common.mixins.models import CommonModelMixin from common.db.encoder import ModelJSONFieldEncoder from orgs.mixins.models import OrgModelMixin @@ -73,6 +77,7 @@ class Ticket(CommonModelMixin, OrgModelMixin): 'TicketFlow', related_name='tickets', on_delete=models.SET_NULL, null=True, verbose_name=_("TicketFlow") ) + serial_num = models.CharField(max_length=256, unique=True, null=True, verbose_name=_('Serial number')) class Meta: ordering = ('-date_created',) @@ -245,3 +250,49 @@ class Ticket(CommonModelMixin, OrgModelMixin): def body(self): _body = self.handler.get_body() return _body + + def get_serial_num_date(self): + date_created = as_current_tz(self.date_created) + date = date_created.strftime('%Y%m%d') + return date + + def get_last_serail_num(self): + date_created = as_current_tz(self.date_created) + date_prefix = date_created.strftime('%Y%m%d') + + ticket = Ticket.objects.select_for_update().filter( + serial_num__startswith=date_prefix + ).order_by('-date_created').first() + + if ticket: + # 202212010001 + num_str = ticket.serial_num[8:] + num = int(num_str) + return num + return None + + def get_next_serail_num(self): + num = self.get_last_serail_num() + if num is None: + num = 0 + return '%04d' % (num + 1) + + def construct_serial_num(self): + date_prefix = self.get_serial_num_date() + num_suffix = self.get_next_serail_num() + return date_prefix + num_suffix + + def update_serial_num_if_need(self): + if self.serial_num: + return + + try: + self.serial_num = self.construct_serial_num() + self.save(update_fields=('serial_num',)) + except IntegrityError as e: + if e.args[0] == 1062: + # 虽然做了 `select_for_update` 但是每天的第一条工单仍可能造成冲突 + # 但概率小,这里只报错,用户重新提交即可 + raise JMSException(detail=_('Please try again'), code='please_try_again') + + raise e diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py index b93ee2543..cc4a90936 100644 --- a/apps/tickets/serializers/ticket/ticket.py +++ b/apps/tickets/serializers/ticket/ticket.py @@ -28,7 +28,8 @@ class TicketSerializer(OrgResourceModelSerializerMixin): fields_small = fields_mini + [ 'type', 'type_display', 'meta', 'state', 'approval_step', 'status', 'status_display', 'applicant_display', 'process_map', - 'date_created', 'date_updated', 'comment', 'org_id', 'org_name', 'body' + 'date_created', 'date_updated', 'comment', 'org_id', 'org_name', 'body', + 'serial_num', ] fields_fk = ['applicant', ] fields = fields_small + fields_fk diff --git a/apps/tickets/signals_handler/ticket.py b/apps/tickets/signals_handler/ticket.py index 8bcbc521b..1a343b18f 100644 --- a/apps/tickets/signals_handler/ticket.py +++ b/apps/tickets/signals_handler/ticket.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- # from django.dispatch import receiver - +from django.db.models.signals import post_save from common.utils import get_logger -from tickets.models import Ticket, ApprovalRule -from ..signals import post_change_ticket_action, post_or_update_change_ticket_flow_approval +from tickets.models import Ticket +from ..signals import post_change_ticket_action logger = get_logger(__name__) @@ -12,3 +12,8 @@ logger = get_logger(__name__) @receiver(post_change_ticket_action, sender=Ticket) def on_post_change_ticket_action(sender, ticket, action, **kwargs): ticket.handler.dispatch(action) + + +@receiver(post_save, sender=Ticket) +def on_pre_save_ensure_serial_num(sender, instance: Ticket, **kwargs): + instance.update_serial_num_if_need()