cma_search/server/apps/system/views.py

660 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
from re import L
from django.conf import settings
from django.contrib.auth.hashers import check_password, make_password
from django.contrib.auth.models import UserManager
from django.core.cache import cache
from django.db.models import Count
from django.http import request
from django.http.response import JsonResponse
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import status
from rest_framework.decorators import (action, api_view, authentication_classes,
permission_classes)
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.mixins import (CreateModelMixin, DestroyModelMixin,
ListModelMixin, RetrieveModelMixin,
UpdateModelMixin)
from rest_framework.pagination import PageNumberPagination
from rest_framework.parsers import (FileUploadParser, JSONParser,
MultiPartParser)
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework_simplejwt.tokens import RefreshToken
from apps.ops.mixins import MyLoggingMixin
from utils.pagination import PageOrNot
from utils.queryset import get_child_queryset2
from .filters import UserFilter
from .models import (City, Dict, DictType, File, Message, Organization, Permission,
Position, Province, Role, User, UserThird)
from .permission import RbacPermission, get_permission_list
from rest_framework_simplejwt.views import TokenObtainPairView
from .permission_data import RbacFilterSet
from .serializers import (CitySerializer, DictSerializer, DictTypeSerializer, FileSerializer,
OrganizationSerializer, PermissionSerializer,
PositionSerializer, ProvinceSerializer, RoleSerializer,
UserCreateSerializer, UserListSerializer,
UserModifySerializer)
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
import requests
import json
from rest_framework.exceptions import AuthenticationFailed
from django.db import transaction
from rest_framework.exceptions import ParseError
from apps.system.permission import has_permission
from openpyxl import load_workbook
import random
import smtplib
import string
from email.header import Header
from email.mime.text import MIMEText
from email.utils import formataddr
from rest_framework_simplejwt.tokens import RefreshToken
logger = logging.getLogger('log')
# logger.info('请求成功! response_code:{}response_headers:{}response_body:{}'.format(response_code, response_headers, response_body[:251]))
# logger.error('请求出错:{}'.format(error))
class ImpMixin:
def get_queryset(self):
mydept = self.request.user.dept
qs = super().get_queryset()
if has_permission('task2', self.request.user):
return qs
return qs.filter(belong_dept=mydept)
def format_date(self, ind, val):
new_val = val
if isinstance(val, datetime.datetime):
new_val = val.date()
elif isinstance(val, datetime.date):
new_val = val
elif isinstance(val, str):
try:
new_val = datetime.datetime.strptime(val, '%Y-%m-%d').date()
except ValueError:
raise ParseError(f'{ind}行, 日期时间格式错误')
elif val is None:
pass
else:
raise ParseError(f'{ind}行, 日期时间格式错误')
return new_val
def get_enum(self, val, atuple, ind):
for i in atuple:
if i[1] == val:
return i[0]
raise ParseError('{}: 请选择固定选项值'.format(ind))
def F(self, data, sheet, i, etype):
raise NotImplementedError()
def gen_imp_view(self, request, start: int, mySerializer):
if 'file' not in request.data:
raise ParseError('请提供文件')
path = request.data['file']
if not str(path).endswith('.xlsx'):
raise ParseError('请提供xlsx格式文件')
fullpath = settings.BASE_DIR + str(path)
wb = load_workbook(fullpath,data_only=True)
sheet = wb.active
# 遍历Excel文件中的数据
data_list = self.build_data(sheet, start)
serializer = mySerializer(data=data_list, many=True, context={'request': request})
if serializer.is_valid():
serializer.save(create_by=request.user, belong_dept=request.user.dept)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response({'uploaded': 'File uploaded successfully'}, status=status.HTTP_201_CREATED)
class LogoutView(APIView):
permission_classes = []
def get(self, request, *args, **kwargs): # 可将token加入黑名单
return Response(status=status.HTTP_200_OK)
def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
import datetime
class MyTokenView(MyLoggingMixin, TokenObtainPairView):
def should_log(self, request, response):
return response.status_code == 200
class Login2View(MyLoggingMixin, APIView):
"""
邮箱验证码登录
"""
authentication_classes = []
permission_classes = []
def post(self, request):
mail = request.data['mail']
msg = request.data['msg']
if not User.objects.filter(username=mail).exists():
return Response('账户不存在', status=status.HTTP_400_BAD_REQUEST)
a_minute_ago=datetime.datetime.now()-datetime.timedelta(minutes=1)
if Message.objects.filter(mail=mail, create_time__gte=a_minute_ago).exists() and Message.objects.filter(mail=mail).last().msg == msg:
user = User.objects.get(username=mail)
return Response(get_tokens_for_user(user), status=status.HTTP_200_OK)
return Response('验证码错误', status=status.HTTP_400_BAD_REQUEST)
def should_log(self, request, response):
return response.status_code == 200
class sendMsg(APIView):
authentication_classes = []
permission_classes = []
def post(self, request):
code = random.randint(10000,99999)
my_sender = 'gxpt@ctc.ac.cn'
my_user = request.data['mail']
my_pass = 'Pintai123'
if not User.objects.filter(username=my_user).exists():
return Response('该账户不存在', status=status.HTTP_400_BAD_REQUEST)
try:
# 邮件内容
msg=MIMEText('您好,共享平台本次登陆验证码为' + str(code),'plain','utf-8')
# 括号里的对应发件人邮箱昵称、发件人邮箱账号
msg['From']=formataddr(["国检集团检验检测能力共享平台",my_sender])
# 括号里的对应收件人邮箱昵称、收件人邮箱账号
msg['To']=formataddr(["",my_user])
# 邮件的主题
msg['Subject'] = Header(str(code), 'utf-8').encode()
# SMTP服务器腾讯企业邮箱端口是465腾讯邮箱支持SSL(不强制) 不支持TLS
# qq邮箱smtp服务器地址:smtp.qq.com,端口号456
# 163邮箱smtp服务器地址smtp.163.com端口号25
server=smtplib.SMTP_SSL("smtp.exmail.qq.com", 465)
# 登录服务器,括号中对应的是发件人邮箱账号、邮箱密码
server.login(my_sender, my_pass)
# 发送邮件,括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
server.sendmail(my_sender,[my_user,],msg.as_string())
# 关闭连接
server.quit()
Message.objects.filter(mail=my_user).delete()
Message.objects.create(mail=my_user, msg=code)
except:
return Response('验证码发送失败', status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_200_OK)
class DictTypeViewSet(ModelViewSet):
"""
数据字典类型:增删改查
"""
perms_map = {'get': '*', 'post': 'dicttype_create',
'put': 'dicttype_update', 'delete': 'dicttype_delete'}
queryset = DictType.objects.all()
serializer_class = DictTypeSerializer
pagination_class = None
search_fields = ['name']
ordering_fields = ['id']
ordering = 'id'
class DictViewSet(ModelViewSet):
"""
数据字典:增删改查
"""
perms_map = {'get': '*', 'post': 'dict_create',
'put': 'dict_update', 'delete': 'dict_delete'}
queryset = Dict.objects.all()
serializer_class = DictSerializer
search_fields = ['name', 'code']
filterset_fields = ['type__code', 'type', 'is_used']
ordering_fields = ['sort']
ordering = 'sort'
def paginate_queryset(self, queryset):
"""
如果查询参数里没有page但有type或type__code时则不分页,否则请求分页
也可用utils.pageornot方法
"""
if self.paginator is None:
return None
elif (not self.request.query_params.get('page', None)) and ((self.request.query_params.get('type__code', None)) or (self.request.query_params.get('type', None))):
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
class PositionViewSet(ModelViewSet):
"""
岗位:增删改查
"""
perms_map = {'get': '*', 'post': 'position_create',
'put': 'position_update', 'delete': 'position_delete'}
queryset = Position.objects.all()
serializer_class = PositionSerializer
pagination_class = None
search_fields = ['name']
ordering_fields = ['id']
ordering = 'id'
class TestView(APIView):
perms_map = {'get': 'test_view'} # 单个API控权
pass
class PermissionViewSet(ModelViewSet):
"""
权限:增删改查
"""
perms_map = {'get': '*', 'post': 'perm_create',
'put': 'perm_update', 'delete': 'perm_delete'}
queryset = Position.objects.all()
queryset = Permission.objects.all()
serializer_class = PermissionSerializer
pagination_class = None
search_fields = ['name']
ordering_fields = ['sort']
ordering = 'sort'
class OrganizationViewSet(PageOrNot,ModelViewSet):
"""
组织机构:增删改查
"""
perms_map = {'get': '*', 'post': 'org_create',
'put': 'org_update', 'delete': 'org_delete'}
queryset = Organization.objects.all()
serializer_class = OrganizationSerializer
pagination_class = None
search_fields = ['name']
filterset_fields = ['pid','name', 'type','can_supervision']
ordering_fields = ['sort']
ordering = ['sort', 'pk']
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
if self.request.query_params.get('service_ok', 0):
queryset = queryset.exclude(service__isnull=True)
return queryset
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
def sub(self, request, pk=None):
"""
获取本人所在部门的下级部门
"""
subdept = get_child_queryset2(request.user.dept).order_by('sort')
serializer = OrganizationSerializer(subdept, many=True)
return Response(serializer.data)
class RoleViewSet(ModelViewSet):
"""
角色:增删改查
"""
perms_map = {'get': '*', 'post': 'role_create',
'put': 'role_update', 'delete': 'role_delete'}
queryset = Role.objects.all()
serializer_class = RoleSerializer
pagination_class = None
search_fields = ['name']
filterset_fields = []
ordering_fields = ['id']
ordering = 'id'
class UserExamViewset(ImpMixin, ModelViewSet):
"""
用户考试:增删改查
"""
perms_map = {'get': '*', 'post': 'user_exam_create',
'put': 'user_exam_create', 'delete': 'user_exam_delete'}
queryset = User.objects.all().order_by('-id')
serializer_class = UserListSerializer
filterset_class = UserFilter
search_fields = ['username', 'name', 'phone', 'email']
ordering_fields = ['-id']
def get_queryset(self):
queryset = self.queryset
dept = self.request.user.dept.id
deptqueryset = get_child_queryset2(Organization.objects.get(pk=dept))
queryset = queryset.filter(dept__in=deptqueryset)
return queryset
def create(self, request, *args, **kwargs):
# 创建用户默认添加密码
password = request.data['password'] if 'password' in request.data else None
if password:
password = make_password(password)
else:
# password = make_password(''.join(random.sample(string.ascii_letters + string.digits, 8)))
password = make_password('0000')
exam_role = Role.objects.get(name='考试')
request.data['roles'] = [exam_role.id]
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(password=password)
return Response(serializer.data)
def build_data(self, sheet, start):
data_list = []
exam_role = Role.objects.get(name='考试')
if not exam_role:
return Response({'msg': '考试角色不存在'})
for row in sheet.iter_rows(min_row=start, values_only=True): # 假设第一行是表头,从第二行开始读取数据
if row[1] is not None:
dept = Organization.objects.get(name=row[3])
user_depts = get_child_queryset2(request.user.dept).order_by('sort')
serializer = OrganizationSerializer(user_depts, many=True)
depts = any(i.id==dept.id for i in serializer.data)
if depts is False:
return Response({'msg': f'本公司下不存在此部门{row[3]}'})
serializer_data = {
'name': row[1],
'username':row[2],
'dept':dept.id,
'roles':[exam_role.id],
'avatar': "/media/default/avatar.png"
}
data_list.append(serializer_data)
return data_list
@action(detail=False, methods=['post'])
@transaction.atomic
def imp(self, request, *args, **kwargs):
"""
导入数据
"""
return self.gen_imp_view(request, 2, UserListSerializer)
class UserViewSet(PageOrNot, ModelViewSet):
"""
用户管理:增删改查
"""
perms_map = {'get': '*', 'post': 'user_create',
'put': 'user_update', 'delete': 'user_delete'}
queryset = User.objects.all().order_by('-id')
serializer_class = UserListSerializer
filterset_class = UserFilter
search_fields = ['username', 'name', 'phone', 'email']
ordering_fields = ['-id']
def get_queryset(self):
queryset = self.queryset
if hasattr(self.get_serializer_class(), 'setup_eager_loading'):
queryset = self.get_serializer_class().setup_eager_loading(queryset) # 性能优化
dept = self.request.query_params.get('dept', None) # 该部门及其子部门所有员工
if dept is not None:
deptqueryset = get_child_queryset2(Organization.objects.get(pk=dept))
queryset = queryset.filter(dept__in=deptqueryset)
return queryset
def get_serializer_class(self):
# 根据请求类型动态变更serializer
if self.action == 'create':
return UserCreateSerializer
elif self.action == 'list':
return UserListSerializer
return UserModifySerializer
def create(self, request, *args, **kwargs):
# 创建用户默认添加密码
password = request.data['password'] if 'password' in request.data else None
if password:
password = make_password(password)
else:
password = make_password(''.join(random.sample(string.ascii_letters + string.digits, 8)))
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(password=password)
return Response(serializer.data)
@action(methods=['put'], detail=False, permission_classes=[IsAuthenticated],
url_name='change_password')
def password(self, request, pk=None):
"""
用户修改密码
"""
user = request.user
old_password = request.data['old_password']
if check_password(old_password, user.password):
new_password1 = request.data['new_password1']
new_password2 = request.data['new_password2']
if new_password1 == new_password2:
user.set_password(new_password2)
user.save()
return Response('密码修改成功!', status=status.HTTP_200_OK)
else:
return Response('新密码两次输入不一致!', status=status.HTTP_400_BAD_REQUEST)
else:
return Response('旧密码错误!', status=status.HTTP_400_BAD_REQUEST)
# perms_map={'get':'*'}, 自定义action控权
@action(methods=['get'], detail=False, url_name='my_info', permission_classes=[IsAuthenticated])
def info(self, request, pk=None):
"""
初始化用户信息
"""
user = request.user
perms = get_permission_list(user)
data = {
'id': user.id,
'username': user.username,
'name': user.name,
'dept': user.dept.pk if user.dept else '',
'dept_name':user.dept.name if user.dept else '',
'roles': user.roles.all().values_list('name', flat=True),
# 'avatar': request._request._current_scheme_host + '/media/' + str(user.image),
'avatar': request._request._current_scheme_host + user.avatar,
'perms': perms,
}
try:
usert = UserThird.objects.get(user=user,type='wx_mp')
data['wxmp_openid'] = usert.openid
except:
pass
return Response(data)
@action(methods=['put'], detail=True, url_name='userpw_reset', permission_classes=[IsAuthenticated])
def resetpw(self, request, pk=None):
"""
重置密码
"""
if request.user.is_superuser:
user = self.get_object()
user.set_password('0000')
user.save()
return Response('密码已重置为0000', status=status.HTTP_200_OK)
return Response('权限不足', status=status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], detail=False, permission_classes=[IsAuthenticated])
def bindwxmp(self, request, pk=None):
"""
绑定微信
"""
code = request.data['code']
info = requests.get('https://api.weixin.qq.com/sns/jscode2session?appid='+settings.WX_APPID+'&secret='+settings.WX_APPSECRET+'&js_code=' +
code+'&grant_type=authorization_code').content.decode('utf-8')
info = json.loads(info)
openid = info['openid']
instances = UserThird.objects.filter(openid=openid)
if instances.exists():
instance = instances[0]
instance.user = request.user
instance.save()
else:
UserThird.objects.get_or_create(openid=openid, type='wx_mp', user=request.user, defaults={'openid':openid, 'user':request.user, 'type':'wx_mp'})
return Response({'wxmp_openid':openid},status=status.HTTP_200_OK)
@action(methods=['post'], detail=False, permission_classes=[IsAuthenticated])
def unbindwxmp(self, request, pk=None):
"""
解除微信绑定
"""
UserThird.objects.filter(user=request.user, type='wx_mp').delete()
return Response()
@action(methods=['post'], detail=False, perms_map={'post':'user_daoru'})
@transaction.atomic
def daoru(self, request, pk=None):
from openpyxl import load_workbook
wb = load_workbook('./media/default/users.xlsx')
sheet = wb.get_sheet_by_name('Sheet2')
i = 3
role1 = Role.objects.get(name='能力查询')
role2 = Role.objects.get(name='资质能力报送')
role3 = Role.objects.get(name='资料分享')
role4 = Role.objects.get(name='视频培训')
while sheet['b'+str(i)].value:
name = sheet['b'+str(i)].value
email = sheet['e'+str(i)].value
dept = Organization.objects.get(name=sheet['j'+str(i)].value)
if not User.objects.filter(username=email).exists():
user = User.objects.create(name=name,
username=email,
password=make_password(''.join(random.sample(string.ascii_letters + string.digits, 8))),
dept=dept)
else:
user = User.objects.get(username=email)
if sheet['f'+str(i)].value:
user.roles.add(role1)
if sheet['g'+str(i)].value:
user.roles.add(role2)
if sheet['h'+str(i)].value:
user.roles.add(role3)
if sheet['i'+str(i)].value:
user.roles.add(role4)
print(str(i)+name+'已导入')
i = i + 1
return Response()
class WXMPlogin(MyLoggingMixin, APIView):
authentication_classes=[]
permission_classes=[]
def post(self,request):
"""
微信登录
"""
code = request.data['code']
info = requests.get('https://api.weixin.qq.com/sns/jscode2session?appid='+settings.WX_APPID+'&secret='+settings.WX_APPSECRET+'&js_code=' +
code+'&grant_type=authorization_code').content.decode('utf-8')
info = json.loads(info)
openid = info.get('openid', None)
if not openid:
Response('微信id获取失败', status=status.HTTP_400_BAD_REQUEST)
session_key = info['session_key']
try:
user = UserThird.objects.get(openid=openid,type='wx_mp').user
return Response(get_tokens_for_user(user), status=status.HTTP_200_OK)
except:
raise AuthenticationFailed
def should_log(self, request, response):
return response.status_code == 200
class ProviceViewSet(PageOrNot, ListModelMixin, GenericViewSet):
queryset = Province.objects.all()
serializer_class = ProvinceSerializer
search_fields = ['name']
ordering = 'id'
@method_decorator(cache_page(60*60*2))
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
class CityViewSet(PageOrNot, ListModelMixin, GenericViewSet):
queryset = City.objects.all()
serializer_class = CitySerializer
filterset_fields = ['parent']
search_fields = ['name']
ordeing = 'id'
@method_decorator(cache_page(60*60*2))
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
class FileViewSet(ModelViewSet):
"""
文件:增删改查
"""
perms_map = None
permission_classes=[IsAuthenticated]
parser_classes = [MultiPartParser, JSONParser]
queryset = File.objects.all()
serializer_class = FileSerializer
filterset_fields = ['type']
search_fields = ['name']
ordering = '-create_time'
def perform_create(self, serializer):
fileobj = self.request.data.get('file')
name = fileobj._name
size = fileobj.size
mime = fileobj.content_type
type = '其它'
if 'image' in mime:
type = '图片'
elif 'video' in mime:
type = '视频'
elif 'audio' in mime:
type = '音频'
elif 'application' or 'text' in mime:
type = '文档'
instance = serializer.save(create_by = self.request.user, name=name, size=size, type=type, mime=mime)
instance.path = settings.MEDIA_URL + instance.file.name
instance.save()
from rest_framework_simplejwt.state import token_backend
from django.http import HttpResponseForbidden, HttpResponse
import urllib.parse as urlparse
def mediaauth(request):
path = request.META.get("HTTP_OURL", '')
if '/cert/' in path:
return HttpResponse()
token = ""
token = request.COOKIES.get('token', None)
if not token:
try:
token = request.META.get("HTTP_OURL", None).split('=')[-1]
except:
pass
if token:
try:
payload = token_backend.decode(token)
if payload.get('user_id', None):
return HttpResponse()
except:
return HttpResponseForbidden()
return HttpResponseForbidden()
class InitAreaView(APIView):
def get(self, request):
from django.db import transaction
with transaction.atomic():
from apps.system.pcity import ps, cs
for i in ps:
Province.objects.create(id=i['code'], name=i['name'])
for i in cs:
City.objects.create(id=i['code'], name=i['name'], parent=Province.objects.get(id=i['provinceCode']))
return Response()