校验上传的证件照片

This commit is contained in:
曹前明 2022-07-07 17:30:02 +08:00
parent 3f0d73416b
commit b04d372e19
12 changed files with 140 additions and 51 deletions

View File

@ -61,10 +61,11 @@ class Access(CommonADModel):
type = models.PositiveSmallIntegerField('准入类型', choices=ACCESS_CHOICE) type = models.PositiveSmallIntegerField('准入类型', choices=ACCESS_CHOICE)
area = models.ForeignKey(Area, verbose_name='关联区域', area = models.ForeignKey(Area, verbose_name='关联区域',
on_delete=models.CASCADE) 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) 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) 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) 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_min = models.PositiveSmallIntegerField('最短停留时间', default=0)
stay_minute_max = models.PositiveSmallIntegerField('最长停留时间', default=0) stay_minute_max = models.PositiveSmallIntegerField('最长停留时间', default=0)
sort = models.PositiveSmallIntegerField('排序', default=1) sort = models.PositiveSmallIntegerField('排序', default=1)

View File

@ -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.opm.serializers import OplCloseSerializer
from apps.wf.models import Ticket, Transition 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): def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict):
opl = Opl.objects.get(id=new_ticket_data['opl']) opl = Opl.objects.get(id=new_ticket_data['opl'])
ticket_data = ticket.ticket_data ticket_data = ticket.ticket_data

View File

@ -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='关联工单'),
),
]

View File

@ -2,6 +2,7 @@ from django.db import models
from apps.hrm.models import Employee from apps.hrm.models import Employee
from apps.utils.models import CommonAModel, CommonBModel, BaseModel from apps.utils.models import CommonAModel, CommonBModel, BaseModel
from apps.system.models import User from apps.system.models import User
from apps.wf.models import Ticket
# Create your models here. # Create your models here.
@ -42,6 +43,8 @@ class Visit(CommonBModel):
count_people = models.PositiveSmallIntegerField('来访人数', null=True, blank=True) count_people = models.PositiveSmallIntegerField('来访人数', null=True, blank=True)
receptionist = models.ForeignKey(User, verbose_name='接待人', on_delete=models.CASCADE) receptionist = models.ForeignKey(User, verbose_name='接待人', on_delete=models.CASCADE)
visitors = models.ManyToManyField('vm.visitor', through='vm.vpeople', related_name='v_visitors') 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 创建人 # create_by 创建人

View File

@ -5,6 +5,9 @@ from apps.vm.models import Visit, Visitor, Vpeople
from apps.hrm.serializers import phone_check from apps.hrm.serializers import phone_check
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ParseError 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): class VisitCreateUpdateSerializer(CustomModelSerializer):
@ -27,6 +30,12 @@ class VisitorCreateSerializer(CustomModelSerializer):
model = Visitor model = Visitor
fields = ['name', 'phone', 'photo', 'id_number'] 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 VisitorSerializer(CustomModelSerializer):
class Meta: class Meta:

38
apps/vm/services.py Normal file
View File

@ -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()

View File

@ -6,25 +6,3 @@ from apps.vm.models import Visit, Vpeople
from celery import shared_task 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()

View File

@ -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='所属部门'),
),
]

View File

@ -1,5 +1,5 @@
from django.db import models 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.system.models import User
from apps.utils.models import BaseModel 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) parent = models.ForeignKey('self', verbose_name='父字段', on_delete=models.SET_NULL, null=True, blank=True)
class Ticket(CommonAModel): class Ticket(CommonBModel):
""" """
工单 工单
""" """

View File

@ -165,7 +165,7 @@ class WfService(object):
module, func = destination_participant.rsplit(".", 1) module, func = destination_participant.rsplit(".", 1)
m = importlib.import_module(module) m = importlib.import_module(module)
f = getattr(m, func) 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: # else:
# destination_participant = getattr(GetParticipants, destination_participant)( # destination_participant = getattr(GetParticipants, destination_participant)(
# state=state, ticket=ticket, new_ticket_data=new_ticket_data, hander=handler) # 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) depts = get_parent_queryset(ticket.belong_dept)
user_queryset = user_queryset.filter(dept__in=depts) user_queryset = user_queryset.filter(dept__in=depts)
elif state.filter_policy == 2: 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) user_queryset = user_queryset.filter(dept__in=depts)
elif state.filter_policy == 3: 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) user_queryset = user_queryset.filter(dept__in=depts)
destination_participant = list(user_queryset.values_list('id', flat=True)) destination_participant = list(user_queryset.values_list('id', flat=True))
if type(destination_participant) == list: if type(destination_participant) == list:
@ -214,7 +214,7 @@ class WfService(object):
current_participant_count = 0 current_participant_count = 0
participant_type = ticket.participant_type participant_type = ticket.participant_type
participant = ticket.participant participant = ticket.participant
state = ticket.stateF state = ticket.state
if participant_type == State.PARTICIPANT_TYPE_PERSONAL: if participant_type == State.PARTICIPANT_TYPE_PERSONAL:
if user.id != participant: if user.id != participant:
return dict(permission=False, msg="非当前处理人", need_accept=False) 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_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
participant=None, participant_cc=destination_state.participant_cc) participant=None, participant_cc=destination_state.participant_cc)
cls.task_ticket(ticket=ticket)
return ticket return ticket
@classmethod @classmethod
@ -372,22 +374,13 @@ class WfService(object):
""" """
执行任务(自定义任务或通知) 执行任务(自定义任务或通知)
""" """
# 如果目标状态是脚本则异步执行
state = ticket.state 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执行额外操作(比如发送通知) # 如果目标状态有func,由func执行额外操作(比如发送通知)
if state.on_reach_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) m = importlib.import_module(module)
f = getattr(m, func) f = getattr(m, func)
f.delay(ticket_id=ticket.id) # 不用加入回调 f(ticket=ticket) # 同步执行
else: else:
# wf默认发送通知 # wf默认发送通知
last_log = TicketFlow.objects.filter(ticket=ticket).order_by('-create_time').first() 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 last_log.intervene_type == Transition.TRANSITION_INTERVENE_TYPE_DELIVER or
ticket.in_add_node): 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) 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) # 里面要加入回调才能继续流转

View File

@ -7,9 +7,9 @@ from apps.wf.serializers import TicketDetailSerializer
@shared_task(base=CustomTask) @shared_task(base=CustomTask)
def send_ticket_notice(ticket): def send_ticket_notice(ticket_id):
""" """
发送通知 发送通知
""" """
data = TicketDetailSerializer(instance=ticket).data pass

View File

@ -42,6 +42,9 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
ordering_fields = ['create_time'] ordering_fields = ['create_time']
ordering = ['-create_time'] ordering = ['-create_time']
def get_object(self):
return super().get_object()
@action(methods=['get'], detail=True, perms_map={'get': 'workflow:update'}, @action(methods=['get'], detail=True, perms_map={'get': 'workflow:update'},
pagination_class=None, serializer_class=StateSerializer) pagination_class=None, serializer_class=StateSerializer)
def states(self, request, pk=None): def states(self, request, pk=None):
@ -93,7 +96,6 @@ class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
ret['field_list'] = field_list ret['field_list'] = field_list
return Response(ret) return Response(ret)
class StateViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet): class StateViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
perms_map = {'get': '*', 'post': 'workflow:update', perms_map = {'get': '*', 'post': 'workflow:update',
'put': 'workflow:update', 'delete': 'workflow:update'} 'put': 'workflow:update', 'delete': 'workflow:update'}
@ -130,7 +132,7 @@ class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin,
class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet): class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
perms_map = {'get': '*', 'post': 'ticket:create'} perms_map = {'get': '*', 'post': '*'}
queryset = Ticket.objects.all() queryset = Ticket.objects.all()
serializer_class = TicketSerializer serializer_class = TicketSerializer
search_fields = ['title'] search_fields = ['title']
@ -184,7 +186,7 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R
create_by=request.user, create_by=request.user,
create_time=timezone.now(), create_time=timezone.now(),
act_state=Ticket.TICKET_ACT_STATE_DRAFT, act_state=Ticket.TICKET_ACT_STATE_DRAFT,
belong_dept=request.user.dept, belong_dept=request.user.belong_dept,
ticket_data=save_ticket_data) # 先创建出来 ticket_data=save_ticket_data) # 先创建出来
# 更新title和sn # 更新title和sn
title = vdata.get('title', '') title = vdata.get('title', '')
@ -198,7 +200,6 @@ class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, R
ticket.save() ticket.save()
ticket = WfService.handle_ticket(ticket=ticket, transition=transition, new_ticket_data=ticket_data, ticket = WfService.handle_ticket(ticket=ticket, transition=transition, new_ticket_data=ticket_data,
handler=request.user, created=True) handler=request.user, created=True)
WfService.task_ticket(ticket)
return Response(TicketSerializer(instance=ticket).data) return Response(TicketSerializer(instance=ticket).data)
@action(methods=['get'], detail=False, perms_map={'get': '*'}) @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'], ticket = WfService.handle_ticket(ticket=ticket, transition=vdata['transition'],
new_ticket_data=new_ticket_data, handler=request.user, new_ticket_data=new_ticket_data, handler=request.user,
suggestion=vdata['suggestion']) suggestion=vdata['suggestion'])
WfService.task_ticket(ticket)
return Response(TicketSerializer(instance=ticket).data) return Response(TicketSerializer(instance=ticket).data)
@action(methods=['post'], detail=True, perms_map={'post': '*'}) @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, suggestion=vdata['suggestion'], participant_type=State.PARTICIPANT_TYPE_PERSONAL,
intervene_type=Transition.TRANSITION_INTERVENE_TYPE_DELIVER, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_DELIVER,
participant=request.user, transition=None) participant=request.user, transition=None)
WfService.task_ticket(ticket)
return Response() return Response()
@action(methods=['get'], detail=True, perms_map={'get': '*'}) @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, suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL,
intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE,
participant=request.user, transition=None) participant=request.user, transition=None)
WfService.task_ticket(ticket)
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=TicketAddNodeEndSerializer) @action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=TicketAddNodeEndSerializer)