diff --git a/apps/am/models.py b/apps/am/models.py index 13642bc8..7e98c181 100755 --- a/apps/am/models.py +++ b/apps/am/models.py @@ -61,10 +61,11 @@ class Access(CommonADModel): type = models.PositiveSmallIntegerField('准入类型', choices=ACCESS_CHOICE) area = models.ForeignKey(Area, verbose_name='关联区域', on_delete=models.CASCADE) - obj_cate = models.CharField('对象类型', max_length=20, help_text='post/org/people') + obj_cate = models.CharField('对象类型', max_length=20, help_text='post/org/people/visit') post = models.ForeignKey(Post, verbose_name='关联岗位', on_delete=models.CASCADE, null=True, blank=True) dept = models.ForeignKey(Dept, verbose_name='关联部门', on_delete=models.CASCADE, null=True, blank=True) employee = models.ForeignKey(Employee, verbose_name='关联人员', on_delete=models.CASCADE, null=True, blank=True) + visit = models.ForeignKey('vm.visit', verbose_name='关联访客项目', on_delete=models.SET_NULL, db_constraint=False, null=True, blank=True) stay_minute_min = models.PositiveSmallIntegerField('最短停留时间', default=0) stay_minute_max = models.PositiveSmallIntegerField('最长停留时间', default=0) sort = models.PositiveSmallIntegerField('排序', default=1) diff --git a/apps/opm/services.py b/apps/opm/services.py index fb154435..ac9eac8a 100644 --- a/apps/opm/services.py +++ b/apps/opm/services.py @@ -1,8 +1,28 @@ -from apps.opm.models import Operation, Opl +from apps.opm.models import Operation, Opl, OplWorker from apps.opm.serializers import OplCloseSerializer from apps.wf.models import Ticket, Transition +def get_op_manager(state, ticket, new_ticket_data, handler): + """_summary_ + + Args: + state (_type_): 工作流节点实例 + ticket (_type_): 工单实例 + new_ticket_data (_type_): 提交的工单数据 + handler (_type_): 处理人实例 + """ + opl = Opl.objects.filter(ticket=ticket).first() + if opl: + return [opl.charger.id] + + +def get_op_workers(state, ticket, new_ticket_data, handler): + opl = Opl.objects.filter(ticket=ticket).first() + if opl: + return list(OplWorker.objects.filter(opl=opl).values_list('worker__id', flat=True)) + + def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict): opl = Opl.objects.get(id=new_ticket_data['opl']) ticket_data = ticket.ticket_data diff --git a/apps/vm/migrations/0003_visit_ticket.py b/apps/vm/migrations/0003_visit_ticket.py new file mode 100644 index 00000000..f72045e5 --- /dev/null +++ b/apps/vm/migrations/0003_visit_ticket.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2022-07-07 07:22 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('wf', '0003_ticket_belong_dept'), + ('vm', '0002_visit_visitors'), + ] + + operations = [ + migrations.AddField( + model_name='visit', + name='ticket', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.ticket', verbose_name='关联工单'), + ), + ] diff --git a/apps/vm/models.py b/apps/vm/models.py index 023c91a2..7c100313 100644 --- a/apps/vm/models.py +++ b/apps/vm/models.py @@ -2,6 +2,7 @@ from django.db import models from apps.hrm.models import Employee from apps.utils.models import CommonAModel, CommonBModel, BaseModel from apps.system.models import User +from apps.wf.models import Ticket # Create your models here. @@ -42,6 +43,8 @@ class Visit(CommonBModel): count_people = models.PositiveSmallIntegerField('来访人数', null=True, blank=True) receptionist = models.ForeignKey(User, verbose_name='接待人', on_delete=models.CASCADE) visitors = models.ManyToManyField('vm.visitor', through='vm.vpeople', related_name='v_visitors') + ticket = models.ForeignKey(Ticket, verbose_name='关联工单', + on_delete=models.SET_NULL, null=True, blank=True) # create_by 创建人 diff --git a/apps/vm/serializers.py b/apps/vm/serializers.py index e4fbe562..097482c5 100644 --- a/apps/vm/serializers.py +++ b/apps/vm/serializers.py @@ -5,6 +5,9 @@ from apps.vm.models import Visit, Visitor, Vpeople from apps.hrm.serializers import phone_check from rest_framework import serializers from rest_framework.exceptions import ParseError +from django.db import transaction +from apps.third.clients import dhClient +from apps.third.tapis import dhapis class VisitCreateUpdateSerializer(CustomModelSerializer): @@ -27,6 +30,12 @@ class VisitorCreateSerializer(CustomModelSerializer): model = Visitor fields = ['name', 'phone', 'photo', 'id_number'] + def create(self, validated_data): + with transaction.atomic(): + # 校验上传的证件照 + dhClient.request(**dhapis['person_img_upload'], file_path_rela=validated_data['photo']) + super().create(validated_data) + class VisitorSerializer(CustomModelSerializer): class Meta: diff --git a/apps/vm/services.py b/apps/vm/services.py new file mode 100644 index 00000000..707b9e06 --- /dev/null +++ b/apps/vm/services.py @@ -0,0 +1,38 @@ +from apps.hrm.models import Employee +from apps.vm.models import Visit, Vpeople + + +def bind_visit(ticket, transition, new_ticket_data: dict): + visit = Visit.objects.get(id=new_ticket_data['visit']) + visit.ticket = ticket + if visit.state == Visit.V_CREATE: + visit.state = Visit.V_AUDIT + visit.save() + + +def get_receptionist(state, ticket, new_ticket_data, handler): + visit = Visit.objects.filter(ticket=ticket).first() + if visit: + return [visit.receptionist.id] + + +def visit_audit_end(ticket): + visit = Visit.objects.get(ticket=ticket) + if visit.state == Visit.V_AUDIT: + visit.state = Visit.V_ENTER + visit.save() + # 更新企业访客人员库 + for i in Vpeople.objects.filter(visit=visit): + visitor = i.visitor + ep = Employee.objects.filter(id_number=visitor.id_number, type='visitor').first() + if ep: + pass + else: + ep = Employee() + ep.name = visitor.name + ep.phone = visitor.phone + ep.photo = visitor.photo + ep.id_number = visitor.id_number + ep.save() + visitor.employee = ep + visitor.save() diff --git a/apps/vm/tasks.py b/apps/vm/tasks.py index 85c15189..57c82cb1 100644 --- a/apps/vm/tasks.py +++ b/apps/vm/tasks.py @@ -6,25 +6,3 @@ from apps.vm.models import Visit, Vpeople from celery import shared_task -@shared_task(base=CustomTask) -def visit_audit_end(ticket_id): - visit = Visit.objects.get(ticket__id=ticket_id) - if visit.state == Visit.V_AUDIT: - visit.state = Visit.V_ENTER - visit.save() - # 更新企业访客人员库 - for i in Vpeople.objects.filter(visit=visit): - visitor = i.visitor - ep = Employee.objects.filter(id_number=visitor.id_number).first() - if ep: - pass - else: - ep = Employee() - ep.name = visitor.name - ep.phone = visitor.phone - ep.photo = visitor.photo - ep.id_number = visitor.id_number - ep.type = 'visitor' - ep.save() - visitor.employee = ep - visitor.save() diff --git a/apps/wf/migrations/0003_ticket_belong_dept.py b/apps/wf/migrations/0003_ticket_belong_dept.py new file mode 100644 index 00000000..e30c0cb3 --- /dev/null +++ b/apps/wf/migrations/0003_ticket_belong_dept.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.12 on 2022-07-07 07:11 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0002_user_post'), + ('wf', '0002_auto_20220707_0957'), + ] + + operations = [ + migrations.AddField( + model_name='ticket', + name='belong_dept', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ticket_belong_dept', to='system.dept', verbose_name='所属部门'), + ), + ] diff --git a/apps/wf/models.py b/apps/wf/models.py index 74dabb0d..623eb462 100755 --- a/apps/wf/models.py +++ b/apps/wf/models.py @@ -1,5 +1,5 @@ from django.db import models -from apps.utils.models import CommonAModel +from apps.utils.models import CommonAModel, CommonBModel from apps.system.models import User from apps.utils.models import BaseModel @@ -223,7 +223,7 @@ class CustomField(CommonAModel): parent = models.ForeignKey('self', verbose_name='父字段', on_delete=models.SET_NULL, null=True, blank=True) -class Ticket(CommonAModel): +class Ticket(CommonBModel): """ 工单 """ diff --git a/apps/wf/services.py b/apps/wf/services.py index 406b0c27..a066dbfb 100755 --- a/apps/wf/services.py +++ b/apps/wf/services.py @@ -165,7 +165,7 @@ class WfService(object): module, func = destination_participant.rsplit(".", 1) m = importlib.import_module(module) f = getattr(m, func) - destination_participant = f(state=state, ticket=ticket, new_ticket_data=new_ticket_data, hander=handler) + destination_participant = f(state=state, ticket=ticket, new_ticket_data=new_ticket_data, handler=handler) # else: # destination_participant = getattr(GetParticipants, destination_participant)( # state=state, ticket=ticket, new_ticket_data=new_ticket_data, hander=handler) @@ -181,10 +181,10 @@ class WfService(object): depts = get_parent_queryset(ticket.belong_dept) user_queryset = user_queryset.filter(dept__in=depts) elif state.filter_policy == 2: - depts = get_parent_queryset(ticket.create_by.dept) + depts = get_parent_queryset(ticket.create_by.belong_dept) user_queryset = user_queryset.filter(dept__in=depts) elif state.filter_policy == 3: - depts = get_parent_queryset(handler.dept) + depts = get_parent_queryset(handler.belong_dept) user_queryset = user_queryset.filter(dept__in=depts) destination_participant = list(user_queryset.values_list('id', flat=True)) if type(destination_participant) == list: @@ -214,7 +214,7 @@ class WfService(object): current_participant_count = 0 participant_type = ticket.participant_type participant = ticket.participant - state = ticket.stateF + state = ticket.state if participant_type == State.PARTICIPANT_TYPE_PERSONAL: if user.id != participant: return dict(permission=False, msg="非当前处理人", need_accept=False) @@ -365,6 +365,8 @@ class WfService(object): participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC, participant=None, participant_cc=destination_state.participant_cc) + cls.task_ticket(ticket=ticket) + return ticket @classmethod @@ -372,22 +374,13 @@ class WfService(object): """ 执行任务(自定义任务或通知) """ - # 如果目标状态是脚本则异步执行 state = ticket.state - if state.participant_type == State.PARTICIPANT_TYPE_ROBOT: - module, func = state.participant.rsplit(".", 1) - m = importlib.import_module(module) - f = getattr(m, func) - ticket.script_run_last_result = False - ticket.save() - f.delay(ticket_id=ticket.id, script_str=state.participant) # 里面要加入回调才能继续流转 - # 如果目标状态有func,由func执行额外操作(比如发送通知) if state.on_reach_func: - module, func = state.func.rsplit(".", 1) + module, func = state.on_reach_func.rsplit(".", 1) m = importlib.import_module(module) f = getattr(m, func) - f.delay(ticket_id=ticket.id) # 不用加入回调 + f(ticket=ticket) # 同步执行 else: # wf默认发送通知 last_log = TicketFlow.objects.filter(ticket=ticket).order_by('-create_time').first() @@ -395,5 +388,14 @@ class WfService(object): last_log.intervene_type == Transition.TRANSITION_INTERVENE_TYPE_DELIVER or ticket.in_add_node): # 如果状态变化或是转交加签的情况再发送通知 - from tasks import send_ticket_notice + from apps.wf.tasks import send_ticket_notice send_ticket_notice.delay(ticket_id=ticket.id) + + # 如果目标状态是脚本则异步执行 + if state.participant_type == State.PARTICIPANT_TYPE_ROBOT: + module, func = state.participant.rsplit(".", 1) + m = importlib.import_module(module) + f = getattr(m, func) + ticket.script_run_last_result = False + ticket.save() + f.delay(ticket_id=ticket.id, script_str=state.participant) # 里面要加入回调才能继续流转 diff --git a/apps/wf/tasks.py b/apps/wf/tasks.py index 579539e2..4f528ecd 100644 --- a/apps/wf/tasks.py +++ b/apps/wf/tasks.py @@ -7,9 +7,9 @@ from apps.wf.serializers import TicketDetailSerializer @shared_task(base=CustomTask) -def send_ticket_notice(ticket): +def send_ticket_notice(ticket_id): """ 发送通知 """ - data = TicketDetailSerializer(instance=ticket).data + pass \ No newline at end of file diff --git a/apps/wf/views.py b/apps/wf/views.py index 94407daa..e49488d1 100755 --- a/apps/wf/views.py +++ b/apps/wf/views.py @@ -42,6 +42,9 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet): ordering_fields = ['create_time'] ordering = ['-create_time'] + def get_object(self): + return super().get_object() + @action(methods=['get'], detail=True, perms_map={'get': 'workflow:update'}, pagination_class=None, serializer_class=StateSerializer) def states(self, request, pk=None): @@ -93,7 +96,6 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet): ret['field_list'] = field_list return Response(ret) - class StateViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet): perms_map = {'get': '*', 'post': 'workflow:update', 'put': 'workflow:update', 'delete': 'workflow:update'} @@ -130,7 +132,7 @@ class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet): - perms_map = {'get': '*', 'post': 'ticket:create'} + perms_map = {'get': '*', 'post': '*'} queryset = Ticket.objects.all() serializer_class = TicketSerializer search_fields = ['title'] @@ -184,7 +186,7 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R create_by=request.user, create_time=timezone.now(), act_state=Ticket.TICKET_ACT_STATE_DRAFT, - belong_dept=request.user.dept, + belong_dept=request.user.belong_dept, ticket_data=save_ticket_data) # 先创建出来 # 更新title和sn title = vdata.get('title', '') @@ -198,7 +200,6 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R ticket.save() ticket = WfService.handle_ticket(ticket=ticket, transition=transition, new_ticket_data=ticket_data, handler=request.user, created=True) - WfService.task_ticket(ticket) return Response(TicketSerializer(instance=ticket).data) @action(methods=['get'], detail=False, perms_map={'get': '*'}) @@ -228,7 +229,6 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R ticket = WfService.handle_ticket(ticket=ticket, transition=vdata['transition'], new_ticket_data=new_ticket_data, handler=request.user, suggestion=vdata['suggestion']) - WfService.task_ticket(ticket) return Response(TicketSerializer(instance=ticket).data) @action(methods=['post'], detail=True, perms_map={'post': '*'}) @@ -252,7 +252,6 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R suggestion=vdata['suggestion'], participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_DELIVER, participant=request.user, transition=None) - WfService.task_ticket(ticket) return Response() @action(methods=['get'], detail=True, perms_map={'get': '*'}) @@ -350,7 +349,6 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE, participant=request.user, transition=None) - WfService.task_ticket(ticket) return Response() @action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=TicketAddNodeEndSerializer)