Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
76d1142741
|
@ -3,4 +3,5 @@ ENV = 'production'
|
|||
|
||||
# base api
|
||||
VUE_APP_BASE_API = 'http://47.95.0.242:2222/api'
|
||||
#VUE_APP_BASE_API = 'http://127.0.0.1:8000/api'
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-18 05:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0002_auto_20210924_1127'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='face_data',
|
||||
field=models.JSONField(blank=True, null=True, verbose_name='人脸识别数据'),
|
||||
),
|
||||
]
|
|
@ -28,9 +28,12 @@ class Employee(CommonAModel):
|
|||
academic = models.CharField('学历', max_length=50, null=True, blank=True)
|
||||
jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
|
||||
job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位')
|
||||
face_data = models.JSONField('人脸识别数据', null=True, blank=True)
|
||||
class Meta:
|
||||
verbose_name = '员工补充信息'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return self.name
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from apps.system.models import User
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
from rest_framework import serializers
|
||||
from .models import Employee
|
||||
from apps.system.serializers import UserListSerializer, UserSimpleSerializer
|
||||
from django.db.models.query import Prefetch
|
||||
|
@ -20,3 +20,6 @@ class EmployeeSerializer(ModelSerializer):
|
|||
# queryset=User.objects.filter(employee_user__isnull=True))
|
||||
# )
|
||||
# return queryset
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from django.db.models import base
|
||||
from rest_framework import urlpatterns
|
||||
from apps.hrm.views import EmployeeViewSet
|
||||
from apps.hrm.views import EmployeeViewSet, FaceLogin
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('employee', EmployeeViewSet, basename='employee')
|
||||
urlpatterns = [
|
||||
path('facelogin/', FaceLogin.as_view()),
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
|
||||
|
|
|
@ -1,9 +1,34 @@
|
|||
from django.shortcuts import render
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin
|
||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||
from apps.hrm.models import Employee
|
||||
from apps.hrm.serializers import EmployeeSerializer
|
||||
from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer
|
||||
import face_recognition
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
import logging
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework import status
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from apps.system.models import User
|
||||
logger = logging.getLogger('log')
|
||||
|
||||
|
||||
def load_face_data(username:int, path:str):
|
||||
"""
|
||||
将某用户face_encoding加载进缓存
|
||||
"""
|
||||
face_datas = cache.get_or_set('face_datas', {}, timeout=None)
|
||||
photo_path = settings.BASE_DIR + path
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
face_datas[username] = my_face_encoding
|
||||
cache.set('face_datas', face_datas, timeout=None)
|
||||
return my_face_encoding
|
||||
|
||||
# Create your views here.
|
||||
class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
"""
|
||||
|
@ -12,4 +37,73 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
|
|||
perms_map = {'get': '*', 'put': 'employee_update'}
|
||||
queryset = Employee.objects.all()
|
||||
serializer_class = EmployeeSerializer
|
||||
ordering = ['-pk']
|
||||
ordering = ['-pk']
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save(update_by = self.request.user)
|
||||
try:
|
||||
photo_path = settings.BASE_DIR + instance.photo
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
instance.face_data = my_face_encoding.tolist()
|
||||
instance.save()
|
||||
except:
|
||||
logger.error('人脸识别出错')
|
||||
|
||||
import uuid
|
||||
import base64
|
||||
import os
|
||||
|
||||
def tran64(s):
|
||||
missing_padding = len(s) % 4
|
||||
if missing_padding != 0:
|
||||
s = s+'='* (4 - missing_padding)
|
||||
return s
|
||||
|
||||
class FaceLogin(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = FaceLoginSerializer
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
人脸识别登录
|
||||
"""
|
||||
# serializer = FaceLoginSerializer(data=request.data)
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
filename = str(uuid.uuid4())
|
||||
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
||||
with open(filepath, 'wb') as f:
|
||||
data = tran64(request.data.get('base64').replace(' ', '+'))
|
||||
f.write(base64.urlsafe_b64decode(data))
|
||||
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
|
||||
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
|
||||
try:
|
||||
unknown_picture = face_recognition.load_image_file(filepath)
|
||||
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
|
||||
os.remove(filepath)
|
||||
except:
|
||||
return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 匹配人脸库
|
||||
user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data')
|
||||
user_l = []
|
||||
face_l = []
|
||||
for i in user_faces:
|
||||
user_l.append(i['user'])
|
||||
face_l.append(i['face_data'])
|
||||
|
||||
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
|
||||
for index, value in enumerate(results):
|
||||
if value:
|
||||
# 识别成功
|
||||
user = User.objects.get(id=user_l[index])
|
||||
refresh = RefreshToken.for_user(user)
|
||||
return Response({
|
||||
'refresh': str(refresh),
|
||||
'access': str(refresh.access_token),
|
||||
'username':user.username
|
||||
})
|
||||
return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST)
|
|
@ -0,0 +1,49 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-18 02:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0022_auto_20211014_0944'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='high_limit',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='上限值'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='high_rule',
|
||||
field=models.IntegerField(blank=True, choices=[(1, '小于'), (2, '小于等于')], null=True, verbose_name='上限规则'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='low_limit',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='下限值'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='low_rule',
|
||||
field=models.IntegerField(blank=True, choices=[(1, '大于'), (2, '大于等于')], null=True, verbose_name='下限规则'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='need_judge',
|
||||
field=models.BooleanField(default=False, verbose_name='需要判定'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='rule_expression',
|
||||
field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充工单的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='usedstep',
|
||||
name='step',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='usedstep', to='mtm.step', verbose_name='子工序'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0023_auto_20211018_1057'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recordformfield',
|
||||
name='rule_expression',
|
||||
field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'),
|
||||
),
|
||||
]
|
|
@ -110,6 +110,14 @@ class RecordFormField(CommonAModel):
|
|||
('selects', '多选下拉'),
|
||||
('textarea', '文本域'),
|
||||
)
|
||||
high_rule_choices = (
|
||||
(1, '小于'),
|
||||
(2, '小于等于'),
|
||||
)
|
||||
low_rule_choices = (
|
||||
(1, '大于'),
|
||||
(2, '大于等于'),
|
||||
)
|
||||
form = models.ForeignKey(RecordForm, on_delete=models.CASCADE, verbose_name='关联表格')
|
||||
field_type = models.CharField('类型', max_length=50, choices=field_type_choices)
|
||||
field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突')
|
||||
|
@ -119,6 +127,13 @@ class RecordFormField(CommonAModel):
|
|||
field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True, null=True,
|
||||
help_text='radio,checkbox,select,multiselect类型可供选择的选项,格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
|
||||
sort = models.IntegerField('排序号', default=1)
|
||||
need_judge = models.BooleanField('需要判定', default=False)
|
||||
high_limit = models.FloatField('上限值', null=True, blank=True)
|
||||
high_rule = models.IntegerField('上限规则', choices=high_rule_choices, null=True, blank=True)
|
||||
low_limit = models.FloatField('下限值', null=True, blank=True)
|
||||
low_rule = models.IntegerField('下限规则', choices=low_rule_choices, null=True, blank=True)
|
||||
rule_expression = models.JSONField('判定表达式', default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突' )
|
||||
|
||||
class Meta:
|
||||
verbose_name = '记录表格字段'
|
||||
verbose_name_plural = verbose_name
|
||||
|
|
|
@ -175,7 +175,7 @@ class RecordFormFieldSerializer(serializers.ModelSerializer):
|
|||
class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RecordFormField
|
||||
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
|
||||
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression']
|
||||
|
||||
def validate(self, data):
|
||||
if RecordFormField.objects.filter(field_key=data['field_key'], form=data['form'], is_deleted=False).exists():
|
||||
|
@ -185,7 +185,7 @@ class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
|
|||
class RecordFormFieldUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RecordFormField
|
||||
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
|
||||
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression']
|
||||
|
||||
class RecordFormFieldSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:44
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0024_alter_recordformfield_rule_expression'),
|
||||
('pm', '0004_subproductionplan_steps'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subproductionplan',
|
||||
name='state',
|
||||
field=models.IntegerField(default=0, verbose_name='状态'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SubProductionProgress',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, 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='删除标记')),
|
||||
('type', models.IntegerField(default=1, verbose_name='物料应用类型')),
|
||||
('count', models.IntegerField(verbose_name='应出入数')),
|
||||
('count_real', models.IntegerField(verbose_name='实际出入数')),
|
||||
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料')),
|
||||
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_subplan', to='pm.subproductionplan', verbose_name='关联子生产计划')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pm', '0005_auto_20211019_0944'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subproductionprogress',
|
||||
name='count_real',
|
||||
field=models.IntegerField(default=0, verbose_name='实际出入数'),
|
||||
),
|
||||
]
|
|
@ -31,6 +31,13 @@ class SubProductionPlan(CommonAModel):
|
|||
"""
|
||||
子生产计划
|
||||
"""
|
||||
state_choices=(
|
||||
(0, '制定中'),
|
||||
(1, '已下达'),
|
||||
(2, '已接收'),
|
||||
(3, '生产中'),
|
||||
(4, '已完成')
|
||||
)
|
||||
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
|
||||
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
|
||||
start_date = models.DateField('计划开工日期')
|
||||
|
@ -38,13 +45,21 @@ class SubProductionPlan(CommonAModel):
|
|||
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE)
|
||||
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
|
||||
steps = models.JSONField('工艺步骤', default=list)
|
||||
state = models.IntegerField('状态', default=0)
|
||||
class Meta:
|
||||
verbose_name = '子生产计划'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# class ProductionProgress(BaseModel):
|
||||
# """
|
||||
# 子计划生产进度
|
||||
# """
|
||||
# subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE)
|
||||
# material = models.
|
||||
class SubProductionProgress(BaseModel):
|
||||
"""
|
||||
子计划生产进度统计表
|
||||
"""
|
||||
type_choices=(
|
||||
(1, '输入物料'),
|
||||
(2, '输出物料')
|
||||
)
|
||||
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan')
|
||||
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
|
||||
type = models.IntegerField('物料应用类型', default=1)
|
||||
count = models.IntegerField('应出入数')
|
||||
count_real = models.IntegerField('实际出入数', default=0)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from apps.pm.models import ProductionPlan, SubProductionPlan
|
||||
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
|
||||
from rest_framework import serializers
|
||||
from apps.sam.serializers import OrderSerializer
|
||||
from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer
|
||||
|
@ -38,3 +38,9 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
|
|||
|
||||
class GenSubPlanSerializer(serializers.Serializer):
|
||||
pass
|
||||
|
||||
class SubProductionProgressSerializer(serializers.ModelSerializer):
|
||||
material_ = MaterialSimpleSerializer(source='material', read_only=True)
|
||||
class Meta:
|
||||
model = SubProductionProgress
|
||||
fields = '__all__'
|
|
@ -2,11 +2,11 @@ from rest_framework import serializers
|
|||
from rest_framework.views import APIView
|
||||
from apps.em.models import Equipment
|
||||
from apps.em.serializers import EquipmentSerializer
|
||||
from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep
|
||||
from apps.mtm.models import InputMaterial, OutputMaterial, Step, SubProduction, UsedStep
|
||||
from apps.system.mixins import CreateUpdateModelAMixin
|
||||
from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer
|
||||
from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
|
||||
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin
|
||||
from apps.pm.models import ProductionPlan, SubProductionPlan
|
||||
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from django.shortcuts import render
|
||||
from apps.sam.models import Order
|
||||
|
@ -72,10 +72,14 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
|
|||
for i in subps:
|
||||
steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False,
|
||||
usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark')
|
||||
SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
|
||||
instance = SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
|
||||
start_date=production_plan.start_date, end_date=production_plan.end_date,
|
||||
workshop=i.process.workshop, process=i.process, create_by=request.user,
|
||||
steps = list(steps))
|
||||
for m in InputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
|
||||
SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance)
|
||||
for m in OutputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
|
||||
SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance)
|
||||
production_plan.is_planed=True
|
||||
production_plan.save()
|
||||
return Response()
|
||||
|
@ -98,7 +102,26 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
|
|||
return SubProductionPlanUpdateSerializer
|
||||
return SubProductionPlanListSerializer
|
||||
|
||||
@action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer)
|
||||
def progress(self, request, pk=None):
|
||||
"""
|
||||
生产进度详情
|
||||
"""
|
||||
obj = self.get_object()
|
||||
serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
|
||||
def issue(self, request, pk=None):
|
||||
"""
|
||||
下达任务
|
||||
"""
|
||||
obj = self.get_object()
|
||||
if obj.state == 0:
|
||||
obj.state = 1
|
||||
obj.save()
|
||||
return Response()
|
||||
raise APIException('计划状态有误')
|
||||
class ResourceViewSet(GenericViewSet):
|
||||
|
||||
perms_map = {'*': '*'}
|
||||
|
|
|
@ -199,5 +199,3 @@ class UserCreateSerializer(serializers.ModelSerializer):
|
|||
return phone
|
||||
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
|
@ -1,5 +1,5 @@
|
|||
from django.urls import path, include
|
||||
from .views import FaceLogin, TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet
|
||||
from .views import TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet
|
||||
from rest_framework import routers
|
||||
|
||||
|
||||
|
@ -15,6 +15,5 @@ router.register('ptask', PTaskViewSet, basename="ptask")
|
|||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
path('task/', TaskList.as_view()),
|
||||
path('test/', TestView.as_view()),
|
||||
path('facelogin/', FaceLogin.as_view())
|
||||
path('test/', TestView.as_view())
|
||||
]
|
||||
|
|
|
@ -21,6 +21,7 @@ from rest_framework.views import APIView
|
|||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework.exceptions import ValidationError, ParseError
|
||||
from apps.hrm.models import Employee
|
||||
from utils.queryset import get_child_queryset2
|
||||
|
||||
from .filters import UserFilter
|
||||
|
@ -29,7 +30,7 @@ from .models import (Dict, DictType, File, Organization, Permission, Position,
|
|||
Role, User)
|
||||
from .permission import RbacPermission, get_permission_list
|
||||
from .permission_data import RbacFilterSet
|
||||
from .serializers import (DictSerializer, DictTypeSerializer, FaceLoginSerializer, FileSerializer,
|
||||
from .serializers import (DictSerializer, DictTypeSerializer, FileSerializer,
|
||||
OrganizationSerializer, PermissionSerializer,
|
||||
PositionSerializer, RoleSerializer, PTaskSerializer,PTaskCreateUpdateSerializer,
|
||||
UserCreateSerializer, UserListSerializer,
|
||||
|
@ -352,43 +353,3 @@ class FileViewSet(CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListM
|
|||
instance.save()
|
||||
|
||||
|
||||
|
||||
#import face_recognition
|
||||
import uuid
|
||||
import base64
|
||||
import os
|
||||
|
||||
def tran64(s):
|
||||
missing_padding = len(s) % 4
|
||||
if missing_padding != 0:
|
||||
s = s+'='* (4 - missing_padding)
|
||||
return s
|
||||
|
||||
class FaceLogin(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = FaceLoginSerializer
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
人脸识别登录
|
||||
"""
|
||||
# serializer = FaceLoginSerializer(data=request.data)
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
filename = str(uuid.uuid4())
|
||||
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
||||
with open(filepath, 'wb') as f:
|
||||
data = tran64(request.data.get('base64').replace(' ', '+'))
|
||||
f.write(base64.urlsafe_b64decode(data))
|
||||
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
|
||||
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
# unknown_picture = face_recognition.load_image_file(filepath)
|
||||
# unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
|
||||
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
|
||||
os.remove(filepath)
|
||||
# if results[0] == True:
|
||||
# return Response('这是曹前明')
|
||||
# else:
|
||||
# return Response('这不是曹前明')
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:58
|
||||
|
||||
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', '0013_alter_ticketflow_transition'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='WfScript',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, 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='删除标记')),
|
||||
('usage', models.IntegerField(choices=[(1, '获取处理人'), (2, '执行操作')], default=1, verbose_name='脚本用途')),
|
||||
('wait', models.BooleanField(default=True, verbose_name='是否等待执行完成')),
|
||||
('name', models.CharField(max_length=100, verbose_name='脚本名称')),
|
||||
('content', models.TextField(verbose_name='脚本内容')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
('workflow', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.workflow', verbose_name='关联工作流')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -207,4 +207,20 @@ class TicketFlow(BaseModel):
|
|||
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant')
|
||||
state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE)
|
||||
ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式')
|
||||
intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices)
|
||||
intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices)
|
||||
|
||||
|
||||
class WfScript(CommonAModel):
|
||||
"""
|
||||
执行脚本
|
||||
"""
|
||||
usage_choices =(
|
||||
(1, '获取处理人'),
|
||||
(2, '执行操作'),
|
||||
)
|
||||
usage = models.IntegerField('脚本用途', default=1, choices=usage_choices)
|
||||
wait = models.BooleanField('是否等待执行完成', default=True)
|
||||
name = models.CharField('脚本名称', max_length=100)
|
||||
workflow = models.ForeignKey(Workflow, verbose_name='关联工作流', null=True, blank=True, on_delete=models.SET_NULL)
|
||||
content = models.TextField('脚本内容')
|
||||
|
||||
|
|
|
@ -141,8 +141,8 @@ class WfService(object):
|
|||
multi_all_person_dict = {}
|
||||
destination_participant_type, destination_participant = state.participant_type, state.participant
|
||||
if destination_participant_type == State.PARTICIPANT_TYPE_FIELD:
|
||||
destination_participant = ticket_data.get(destination_participant, None) if destination_participant in ticket_data else Ticket.ticket_data.get(destination_participant, None)
|
||||
|
||||
destination_participant = ticket_data.get(destination_participant, 0) if destination_participant in ticket_data \
|
||||
else Ticket.ticket_data.get(destination_participant, 0)
|
||||
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#单部门
|
||||
destination_participant = list(User.objects.filter(dept=destination_participant).values_list('id', flat=True))
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from django.db.models import query
|
||||
from apps.system.models import User
|
||||
from apps.wf.filters import TicketFilterSet
|
||||
from django.core.exceptions import AppRegistryNotReady
|
||||
|
@ -13,7 +14,7 @@ from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin,
|
|||
from apps.wf.services import WfService
|
||||
from rest_framework.exceptions import APIException, PermissionDenied
|
||||
from rest_framework import status
|
||||
|
||||
from django.db.models import Count
|
||||
# Create your views here.
|
||||
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||
perms_map = {'get': '*', 'post': 'workflow_create',
|
||||
|
@ -136,7 +137,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
|
||||
participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
|
||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||
destination_participant = participant_info.get('destination_participant', None)
|
||||
destination_participant = participant_info.get('destination_participant', 0)
|
||||
multi_all_person = participant_info.get('multi_all_person', {}) # 多人需要全部处理情况
|
||||
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
|
||||
if next_state.type == State.STATE_TYPE_END:
|
||||
|
@ -165,6 +166,17 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
participant=ticket.create_by, transition=transition)
|
||||
return Response(TicketSerializer(instance=ticket).data)
|
||||
|
||||
@action(methods=['get'], detail=False, perms_map={'get':'*'})
|
||||
def duty_agg(self, request, pk=None):
|
||||
"""
|
||||
工单待办聚合
|
||||
"""
|
||||
ret = {}
|
||||
queryset = Ticket.objects.filter(participant__contains=request.user.id, is_deleted=False)\
|
||||
.exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
|
||||
ret['total_count'] = queryset.count()
|
||||
ret['details'] = list(queryset.annotate(count = Count('workflow')).values('workflow', 'workflow__name', 'count'))
|
||||
return Response(ret)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'})
|
||||
def handle(self, request, pk=None):
|
||||
|
@ -197,7 +209,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
if WfService.check_dict_has_all_same_value(multi_all_person):
|
||||
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
|
||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||
destination_participant = participant_info.get('destination_participant', None)
|
||||
destination_participant = participant_info.get('destination_participant', 0)
|
||||
multi_all_person = {}
|
||||
else:
|
||||
# 处理人没有没有全部处理完成或者处理动作不一致
|
||||
|
@ -211,7 +223,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
# 当前处理人类型非全部处理
|
||||
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
|
||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||
destination_participant = participant_info.get('destination_participant', None)
|
||||
destination_participant = participant_info.get('destination_participant', 0)
|
||||
multi_all_person = participant_info.get('multi_all_person', {})
|
||||
|
||||
# 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况
|
||||
|
|
Loading…
Reference in New Issue