import importlib import json from django.contrib.auth.hashers import check_password, make_password from django.db import transaction from django_celery_beat.models import (CrontabSchedule, IntervalSchedule, PeriodicTask) from django_celery_results.models import TaskResult from rest_framework.decorators import action from rest_framework.exceptions import ParseError, ValidationError from rest_framework.mixins import (CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin) from rest_framework.parsers import (JSONParser, MultiPartParser) from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView from apps.system.errors import FUNC_ERROR, OLD_PASSWORD_WRONG, PASSWORD_NOT_SAME, SCHEDULE_WRONG from apps.system.filters import UserFilterSet # from django_q.models import Task as QTask, Schedule as QSchedule from apps.utils.mixins import (CustomCreateModelMixin) from django.conf import settings from apps.utils.permission import ALL_PERMS, get_user_perms_map from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from server.celery import app as celery_app from .models import (Dept, Dictionary, DictType, File, Permission, Post, PostRole, Role, User, UserPost) from .serializers import (DeptCreateUpdateSerializer, DeptSerializer, DictCreateUpdateSerializer, DictSerializer, DictTypeCreateUpdateSerializer, DictTypeSerializer, FileSerializer, PasswordChangeSerializer, PermissionCreateUpdateSerializer, PermissionSerializer, PostCreateUpdateSerializer, PostRoleCreateSerializer, PostRoleSerializer, PostSerializer, PTaskSerializer, PTaskCreateUpdateSerializer, PTaskResultSerializer, RoleCreateUpdateSerializer, RoleSerializer, TaskRunSerializer, UserCreateSerializer, UserListSerializer, UserPostCreateSerializer, UserPostSerializer, UserUpdateSerializer) # logger.info('请求成功! response_code:{};response_headers:{}; # response_body:{}'.format(response_code, response_headers, response_body[:251])) # logger.error('请求出错-{}'.format(error)) class TaskList(APIView): permission_classes = [IsAuthenticated] def get(self, request): """获取注册任务列表 获取注册任务列表 """ tasks = list( sorted(name for name in celery_app.tasks if not name.startswith('celery.'))) return Response(tasks) # class QScheduleViewSet(CustomModelViewSet): # """ # list:定时任务列表 # 定时任务列表 # retrieve:定时任务详情 # 定时任务详情 # """ # queryset = QSchedule.objects.all() # serializer_class = QScheduleSerializer # search_fields = ['name', 'func'] # filterset_fields = ['schedule_type'] # ordering = ['-pk'] # @action(methods=['get'], detail=True, perms_map={'post': 'qschedule:run_once'}) # def run_once(self, request, pk=None): # """同步执行一次 # 同步执行一次 # """ # obj = self.get_object() # module, func = obj.func.rsplit(".", 1) # m = importlib.import_module(module) # f = getattr(m, func) # f(*obj.args.split(','), **eval(f"dict({obj.kwargs})")) # return Response() # class QTaskResultViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): # """ # list:任务执行结果列表 # 任务执行结果列表 # retrieve:任务执行结果详情 # 任务执行结果详情 # """ # perms_map = {'get': '*'} # filterset_fields = ['func'] # queryset = QTask.objects.all() # serializer_class = QTaskResultSerializer # ordering = ['-started'] # lookup_field = 'id' class PTaskViewSet(CustomModelViewSet): """ list:定时任务列表 定时任务列表 retrieve:定时任务详情 定时任务详情 """ queryset = PeriodicTask.objects.exclude(name__contains='celery.') serializer_class = PTaskSerializer create_serializer_class = PTaskCreateUpdateSerializer update_serializer_class = PTaskCreateUpdateSerializer partial_update_serializer_class = PTaskCreateUpdateSerializer search_fields = ['name', 'task'] filterset_fields = ['enabled'] select_related_fields = ['interval', 'crontab'] ordering = ['-id'] @action(methods=['post'], detail=True, perms_map={'get': 'qtask.run_once'}, serializer_class=TaskRunSerializer) def run_once(self, request, pk=None): """执行一次 执行一次 """ obj = self.get_object() module, func = obj.task.rsplit(".", 1) m = importlib.import_module(module) f = getattr(m, func) if request.data.get('sync', True): f(*json.loads(obj.args), **json.loads(obj.kwargs)) return Response() else: task_obj = f.delay(*json.loads(obj.args), **json.loads(obj.kwargs)) return Response({'task_id': task_obj.id}) @action(methods=['put'], detail=True, perms_map={'put': 'ptask.update'}) def toggle(self, request, pk=None): """修改启用禁用状态 修改启用禁用状态 """ obj = self.get_object() obj.enabled = False if obj.enabled else True obj.save() return Response() @transaction.atomic def create(self, request, *args, **kwargs): """创建定时任务 创建定时任务 """ data = request.data timetype = data.get('timetype', None) interval_ = data.get('interval_', None) crontab_ = data.get('crontab_', None) if timetype == 'interval' and interval_: data['crontab'] = None try: interval, _ = IntervalSchedule.objects.get_or_create( **interval_, defaults=interval_) data['interval'] = interval.id except Exception: raise ParseError(**SCHEDULE_WRONG) if timetype == 'crontab' and crontab_: data['interval'] = None try: crontab_['timezone'] = 'Asia/Shanghai' crontab, _ = CrontabSchedule.objects.get_or_create( **crontab_, defaults=crontab_) data['crontab'] = crontab.id except Exception: raise ParseError(**SCHEDULE_WRONG) serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() return Response() @transaction.atomic def update(self, request, *args, **kwargs): """更新定时任务 更新定时任务 """ data = request.data timetype = data.get('timetype', None) interval_ = data.get('interval_', None) crontab_ = data.get('crontab_', None) if timetype == 'interval' and interval_: data['crontab'] = None try: if 'id' in interval_: del interval_['id'] interval, _ = IntervalSchedule.objects.get_or_create( **interval_, defaults=interval_) data['interval'] = interval.id except Exception: raise ParseError(**SCHEDULE_WRONG) if timetype == 'crontab' and crontab_: data['interval'] = None try: crontab_['timezone'] = 'Asia/Shanghai' if 'id' in crontab_: del crontab_['id'] crontab, _ = CrontabSchedule.objects.get_or_create( **crontab_, defaults=crontab_) data['crontab'] = crontab.id except Exception: raise ParseError(**SCHEDULE_WRONG) instance = self.get_object() serializer = self.get_serializer(instance, data=data) serializer.is_valid(raise_exception=True) serializer.save() return Response() class PTaskResultViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): """ list:任务执行结果列表 任务执行结果列表 retrieve:任务执行结果详情 任务执行结果详情 """ perms_map = {'get': '*'} filterset_fields = ['task_name', 'periodic_task_name', 'status'] queryset = TaskResult.objects.all() serializer_class = PTaskResultSerializer ordering = ['-date_created'] lookup_field = 'task_id' class DictTypeViewSet(CustomModelViewSet): """数据字典类型-增删改查 数据字典类型-增删改查 """ queryset = DictType.objects.all() serializer_class = DictTypeSerializer create_serializer_class = DictTypeCreateUpdateSerializer update_serializer_class = DictTypeCreateUpdateSerializer partial_update_serializer_class = DictTypeCreateUpdateSerializer search_fields = ['name'] class DictViewSet(CustomModelViewSet): """数据字典-增删改查 数据字典-增删改查 """ # queryset = Dictionary.objects.get_queryset(all=True) # 获取全部的,包括软删除的 queryset = Dictionary.objects.all() filterset_fields = ['type', 'is_used', 'type__code'] serializer_class = DictSerializer create_serializer_class = DictCreateUpdateSerializer update_serializer_class = DictCreateUpdateSerializer partial_update_serializer_class = DictCreateUpdateSerializer search_fields = ['name'] ordering = ['sort', 'create_time'] class PostViewSet(CustomModelViewSet): """岗位-增删改查 岗位-增删改查 """ queryset = Post.objects.all() serializer_class = PostSerializer create_serializer_class = PostCreateUpdateSerializer update_serializer_class = PostCreateUpdateSerializer partial_update_serializer_class = PostCreateUpdateSerializer search_fields = ['name', 'code', 'description'] ordering = ['create_time'] class PermissionViewSet(CustomModelViewSet): """菜单权限-增删改查 菜单权限-增删改查 """ queryset = Permission.objects.all() filterset_fields = ['type'] serializer_class = PermissionSerializer create_serializer_class = PermissionCreateUpdateSerializer update_serializer_class = PermissionCreateUpdateSerializer partial_update_serializer_class = PermissionCreateUpdateSerializer search_fields = ['name', 'code'] ordering = ['sort', 'create_time'] @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def codes(self, request, pk=None): """获取全部权限标识 需要先请求一次swagger """ ALL_PERMS.sort() return Response(ALL_PERMS) class DeptViewSet(CustomModelViewSet): """部门-增删改查 部门-增删改查 """ queryset = Dept.objects.all() serializer_class = DeptSerializer create_serializer_class = DeptCreateUpdateSerializer update_serializer_class = DeptCreateUpdateSerializer partial_update_serializer_class = DeptCreateUpdateSerializer filterset_fields = ['type'] search_fields = ['name'] ordering = ['type', 'sort', 'create_time'] # def filter_queryset(self, queryset): # if not self.detail: # self.request.query_params._mutable = True # self.request.query_params.setdefault('type', 'dept') # return super().filter_queryset(queryset) # def get_queryset(self): # type = self.request.query_params.get('type', None) # if type: # queryset = Dept.objects.filter(type='rparty') # else: # queryset = Dept.objects.filter(type__in=['dept', 'company']) # return queryset class RoleViewSet(CustomModelViewSet): """角色-增删改查 角色-增删改查 """ queryset = Role.objects.all() serializer_class = RoleSerializer create_serializer_class = RoleCreateUpdateSerializer update_serializer_class = RoleCreateUpdateSerializer partial_update_serializer_class = RoleCreateUpdateSerializer search_fields = ['name', 'code'] ordering = ['create_time'] class PostRoleViewSet(CreateModelMixin, DestroyModelMixin, ListModelMixin, CustomGenericViewSet): """岗位/角色关系 岗位/角色关系 """ perms_map = {'get': '*', 'post': 'post.update', 'delete': 'post.update'} queryset = PostRole.objects.select_related('post', 'role').all() serializer_class = PostRoleSerializer create_serializer_class = PostRoleCreateSerializer filterset_fields = ['post', 'role'] class UserPostViewSet(CreateModelMixin, DestroyModelMixin, ListModelMixin, CustomGenericViewSet): """用户/岗位关系 用户/岗位关系 """ perms_map = {'get': '*', 'post': 'user.update', 'delete': 'user.update'} queryset = UserPost.objects.select_related('user', 'post', 'dept').all() serializer_class = UserPostSerializer create_serializer_class = UserPostCreateSerializer filterset_fields = ['user', 'post', 'dept'] ordering = ['sort', 'create_time'] def perform_create(self, serializer): with transaction.atomic(): instance = serializer.save() user = instance.user up = UserPost.objects.filter(user=user).order_by('sort', 'create_time').first() if up: user.belong_dept = up.dept user.post = up.post user.update_by = self.request.user user.save() def perform_destroy(self, instance): with transaction.atomic(): user = instance.user instance.delete() up = UserPost.objects.filter(user=user).order_by('sort', 'create_time').first() if up: user.belong_dept = up.dept user.post = up.post else: user.belong_dept = None user.post = None user.update_by = self.request.user user.save() class UserViewSet(CustomModelViewSet): queryset = User.objects.all() serializer_class = UserListSerializer create_serializer_class = UserCreateSerializer update_serializer_class = UserUpdateSerializer filterset_class = UserFilterSet search_fields = ['username', 'name', 'phone', 'email'] select_related_fields = ['superior', 'belong_dept'] prefetch_related_fields = ['posts', 'roles', 'depts'] ordering = ['type'] # def filter_queryset(self, queryset): # if not self.detail: # self.request.query_params._mutable = True # self.request.query_params.setdefault('type', 'employee') # return super().filter_queryset(queryset) def create(self, request, *args, **kwargs): """创建用户 创建用户 """ password = make_password('0000') serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save(password=password, belong_dept=None) return Response(data=serializer.data) @action(methods=['put'], detail=False, permission_classes=[IsAuthenticated], serializer_class=PasswordChangeSerializer) 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() else: raise ParseError(**PASSWORD_NOT_SAME) else: raise ValidationError(**OLD_PASSWORD_WRONG) @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def info(self, request, pk=None): """登录用户信息 获取登录用户信息 """ user = request.user perms = get_user_perms_map(user) data = { 'id': user.id, 'username': user.username, 'type': user.type, 'name': user.name, 'roles': user.roles.values_list('name', flat=True), 'avatar': user.avatar, 'perms': perms, 'belong_dept_name': user.belong_dept.name if user.belong_dept else '', 'post_name': user.post.name if user.post else '', 'is_superuser': user.is_superuser, 'wxmp_openid': user.wxmp_openid, 'wx_openid': user.wx_openid } return Response(data) @action(methods=['post'], detail=False, permission_classes=[IsAuthenticated]) def bind_wxmp(self, request, pk=None): """ 绑定微信小程序 绑定微信小程序 """ openid = request.data['openid'] if openid: user = request.user if user.wxmp_openid != openid: User.objects.filter(wxmp_openid=openid).update(wxmp_openid=None) user.wxmp_openid = openid user.save() return Response({'wxmp_openid': openid}) @action(methods=['post'], detail=False, permission_classes=[IsAuthenticated]) def unbind_wxmp(self, request, pk=None): """ 解绑微信小程序 解绑微信小程序 """ user = request.user user.wxmp_openid = None user.save() return Response() @action(methods=['post'], detail=False, permission_classes=[IsAuthenticated]) def bind_wx(self, request, pk=None): """绑定微信公众号 绑定微信公众号, 用于发送通知 """ openid = request.data['openid'] if openid: user = request.user if user.wx_openid != openid: User.objects.filter(wx_openid=openid).update(wx_openid=None) user.wx_openid = openid user.save() return Response({'wx_openid': openid}) @action(methods=['post'], detail=False, permission_classes=[IsAuthenticated]) def bind_secret(self, request, pk=None): """创建密钥 创建密钥 """ secret = request.data['secret'] if secret: user = request.user user.secret = secret user.save() return Response() class FileViewSet(CustomCreateModelMixin, RetrieveModelMixin, ListModelMixin, CustomGenericViewSet): """文件上传 list: 文件列表 文件列表 create: 文件上传 文件上传 """ permission_classes = [IsAuthenticated] parser_classes = [MultiPartParser, JSONParser] queryset = File.objects.all() serializer_class = FileSerializer filterset_fields = ['type'] search_fields = ['name'] def perform_create(self, serializer): file_obj = self.request.data.get('file') name = file_obj._name size = file_obj.size mime = file_obj.content_type file_type = File.FILE_TYPE_OTHER if 'image' in mime: file_type = File.FILE_TYPE_PIC elif 'video' in mime: file_type = File.FILE_TYPE_VIDEO elif 'audio' in mime: file_type = File.FILE_TYPE_AUDIO elif 'application' or 'text' in mime: file_type = File.FILE_TYPE_DOC instance = serializer.save( create_by=self.request.user, name=name, size=size, type=file_type, mime=mime) instance.path = settings.MEDIA_URL + instance.file.name instance.save()