from rest_framework import viewsets from rest_framework.decorators import action from .models import ListModel, TypeListModel from . import serializers from utils.page import MyPageNumberPagination from rest_framework.filters import OrderingFilter from django_filters.rest_framework import DjangoFilterBackend from rest_framework.response import Response from .filter import Filter, TypeFilter from rest_framework.exceptions import APIException from .serializers import FileRenderSerializer from django.http import StreamingHttpResponse from .files import FileRenderCN, FileRenderEN from rest_framework.settings import api_settings from rest_framework import permissions from staff.models import ListModel as staff from userprofile.models import Users from django.utils import timezone from utils.md5 import Md5 import random from django.contrib.auth.models import User from .models import Role, Permission # 新增角色和权限模型导入 from operation_log.views import log_success_operation, log_failure_operation, log_operation class APIViewSet(viewsets.ModelViewSet): """ retrieve: Response a data list(get) list: Response a data list(all) create: Create a data line(post) delete: Delete a data line(delete) partial_update: Partial_update a data(patch:partial_update) update: Update a data(put:update) """ pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "create_time", "update_time", ] filter_class = Filter def list(self, request, *args, **kwargs): # staff_name = str(request.GET.get('staff_name')) # check_code = request.GET.get('check_code') # if staff_name == None and check_code == None: # return super().list(request, *args, **kwargs) # elif staff_name != None and check_code == None: # return super().list(request, *args, **kwargs) # else: # staff_name_obj = ListModel.objects.filter(openid=self.request.auth.openid, staff_name=staff_name, # is_delete=False).first() # if staff_name_obj is None: # raise APIException({"detail": "用户名不存在"}) # elif staff_name_obj.is_lock is True: # raise APIException({"detail": "用户已被锁定,请联系管理员"}) # elif staff_name_obj.error_check_code_counter == 3: # staff_name_obj.is_lock = True # staff_name_obj.error_check_code_counter = 0 # staff_name_obj.save() # raise APIException({"detail": "用户已被锁定,请联系管理员"}) # if type(check_code) == str: # check_code = int(check_code) # if check_code != None: # if staff_name_obj.check_code != check_code: # staff_name_obj.error_check_code_counter = int(staff_name_obj.error_check_code_counter) + 1 # staff_name_obj.save() # raise APIException({"detail": "验证码错误"}) # else: # staff_name_obj.error_check_code_counter = 0 # staff_name_obj.save() # return super().list(request, *args, **kwargs) # else: return super().list(request, *args, **kwargs) def get_project(self): try: id = self.kwargs.get('pk') return id except: return None def get_queryset(self): id = self.get_project() if self.request.user: if id is None: return ListModel.objects.filter(is_delete=False) else: return ListModel.objects.filter(id=id, is_delete=False) else: return ListModel.objects.none() def get_serializer_class(self): staff_name = self.request.auth.name staff_type = ListModel.objects.filter(staff_name=staff_name, is_delete=False).first().staff_type if staff_type not in ['admin', '主管', '管理员','经理']: if self.action in ['list', 'retrieve', 'destroy']: return serializers.userStaffGetSerializer elif self.action in ['create']: return serializers.userStaffPostSerializer elif self.action in ['update']: return serializers.userStaffUpdateSerializer elif self.action in ['partial_update']: return serializers.userStaffPartialUpdateSerializer else: return self.http_method_not_allowed(request=self.request) else: if self.action in ['list', 'retrieve', 'destroy']: return serializers.StaffGetSerializer elif self.action in ['create']: return serializers.StaffPostSerializer elif self.action in ['update']: return serializers.StaffUpdateSerializer elif self.action in ['partial_update']: return serializers.StaffPartialUpdateSerializer else: return self.http_method_not_allowed(request=self.request) def create(self, request, *args, **kwargs): data = self.request.data data['openid'] = self.request.auth.openid # 检查角色是否存在 role_name = data.get('role') if role_name: role, created = Role.objects.get_or_create(name=role_name) data['role'] = role.id if ListModel.objects.filter(openid=data['openid'], staff_name=data['staff_name'], is_delete=False).exists(): raise APIException({"detail": "Data exists"}) else: app_code = Md5.md5(data['staff_name'] + '1') data['appid'] = app_code check_code = random.randint(1000, 9999) data['check_code'] = check_code # 创建/更新 Django auth 用户 username = str(data['staff_name']) password = str(check_code) try: user = User.objects.get(username=username) user.set_password(password) user.save() except User.DoesNotExist: user = User.objects.create_user(username=username, password=password) ip = request.META.get('HTTP_X_FORWARDED_FOR') if request.META.get( 'HTTP_X_FORWARDED_FOR') else request.META.get('REMOTE_ADDR') # 创建/更新用户档案 users_defaults = dict( name=str(data['staff_name']), openid=app_code, appid=app_code, t_code=Md5.md5(str(timezone.now())), developer=1, ip=ip ) profile, created = Users.objects.get_or_create(user_id=user.id, defaults=users_defaults) if not created: # 同步基础字段 profile.name = users_defaults['name'] profile.openid = users_defaults['openid'] profile.appid = users_defaults['appid'] profile.ip = users_defaults['ip'] profile.save() serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() headers = self.get_success_headers(serializer.data) try: log_success_operation( request=self.request, operation_content=f"创建员工:{data.get('staff_name', '未知')}", operation_level="new", operator=self.request.auth.name if hasattr(self.request, 'auth') and self.request.auth else None, object_id=serializer.data.get('id'), module_name="员工管理" ) except Exception as e: pass # 日志记录失败不影响业务 return Response(serializer.data, status=200, headers=headers) def update(self, request, pk): qs = self.get_object() # if qs.openid != self.request.auth.openid: # creator = ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False) # raise APIException({"detail": "该用户不是您创建的,不能修改"}) # else: data = self.request.data # 更新角色 role_name = data.get('role') if role_name: role, created = Role.objects.get_or_create(name=role_name) data['role'] = role.id old_staff_name = qs.staff_name serializer = self.get_serializer(qs, data=data) serializer.is_valid(raise_exception=True) serializer.save() # 同步 Django auth 用户与用户档案 new_staff_name = serializer.instance.staff_name if old_staff_name != new_staff_name: try: auth_user = User.objects.get(username=str(old_staff_name)) auth_user.username = str(new_staff_name) auth_user.save() # 同步 userprofile.Users profile = Users.objects.filter(user_id=auth_user.id).first() if profile: profile.name = str(new_staff_name) profile.save() except User.DoesNotExist: # 如不存在旧账号,则以新名称创建 auth_user = User.objects.create_user(username=str(new_staff_name), password=str(qs.check_code)) Users.objects.get_or_create(user_id=auth_user.id, defaults=dict( name=str(new_staff_name), openid=qs.appid, appid=qs.appid, t_code=Md5.md5(str(timezone.now())), developer=1, ip=request.META.get('REMOTE_ADDR') )) headers = self.get_success_headers(serializer.data) try: log_success_operation( request=self.request, operation_content=f"更新员工 ID:{pk}:{data.get('staff_name', '未知')}", operation_level="update", operator=self.request.auth.name if hasattr(self.request, 'auth') and self.request.auth else None, object_id=pk, module_name="员工管理" ) except Exception as e: pass return Response(serializer.data, status=200, headers=headers) def change_check_code(self, request, pk=None): """ 修改员工验证码(密码) - 普通员工:只能修改自己的验证码 - 管理/主管/管理员/经理:可修改任意员工验证码 请求体: { "new_check_code": 1234 } """ target_staff = self.get_object() current_staff = ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False).first() if current_staff is None: raise APIException({"detail": "当前用户不存在或未登录"}) privileged_types = ['admin', '主管', '管理员', '经理'] can_modify_any = str(current_staff.staff_type) in privileged_types if not can_modify_any and current_staff.id != target_staff.id: raise APIException({"detail": "无权限修改他人验证码"}) new_code = request.data.get('new_check_code') try: new_code_int = int(new_code) except (TypeError, ValueError): raise APIException({"detail": "new_check_code 必须为4位数字"}) if new_code_int < 0 or new_code_int > 9999: raise APIException({"detail": "new_check_code 必须为0-9999之间的数字"}) # 更新staff表 target_staff.check_code = new_code_int target_staff.error_check_code_counter = 0 target_staff.is_lock = False target_staff.save() # 同步更新Django auth用户密码(用户名为staff_name) try: auth_user = User.objects.get(username=str(target_staff.staff_name)) auth_user.set_password(str(new_code_int)) auth_user.save() except User.DoesNotExist: # 若不存在则创建,保持行为幂等 User.objects.create_user(username=str(target_staff.staff_name), password=str(new_code_int)) try: log_success_operation( request=self.request, operation_content=f"修改员工验证码 ID:{target_staff.id},员工:{target_staff.staff_name}", operation_level="update", operator=self.request.auth.name if hasattr(self.request, 'auth') and self.request.auth else None, object_id=target_staff.id, module_name="员工管理" ) except Exception as e: pass return Response({"message": "验证码已更新", "id": target_staff.id, "check_code": target_staff.check_code}) def partial_update(self, request, pk): qs = self.get_object() if qs.openid != self.request.auth.openid: raise APIException({"detail": "Cannot Update Data Which Not Yours"}) else: data = self.request.data # 更新角色 role_name = data.get('role') if role_name: role, created = Role.objects.get_or_create(name=role_name) data['role'] = role.id old_staff_name = qs.staff_name serializer = self.get_serializer(qs, data=data, partial=True) serializer.is_valid(raise_exception=True) serializer.save() # 同步 Django auth 用户与用户档案 new_staff_name = serializer.instance.staff_name if old_staff_name != new_staff_name: try: auth_user = User.objects.get(username=str(old_staff_name)) auth_user.username = str(new_staff_name) auth_user.save() profile = Users.objects.filter(user_id=auth_user.id).first() if profile: profile.name = str(new_staff_name) profile.save() except User.DoesNotExist: auth_user = User.objects.create_user(username=str(new_staff_name), password=str(qs.check_code)) Users.objects.get_or_create(user_id=auth_user.id, defaults=dict( name=str(new_staff_name), openid=qs.appid, appid=qs.appid, t_code=Md5.md5(str(timezone.now())), developer=1, ip=request.META.get('REMOTE_ADDR') )) headers = self.get_success_headers(serializer.data) try: log_success_operation( request=self.request, operation_content=f"部分更新员工 ID:{pk}", operation_level="update", operator=self.request.auth.name if hasattr(self.request, 'auth') and self.request.auth else None, object_id=pk, module_name="员工管理" ) except Exception as e: pass return Response(serializer.data, status=200, headers=headers) def destroy(self, request, pk): qs = self.get_object() if qs.openid != self.request.auth.openid: raise APIException({"detail": "Cannot Delete Data Which Not Yours"}) else: qs.is_delete = True qs.save() serializer = self.get_serializer(qs, many=False) headers = self.get_success_headers(serializer.data) try: log_success_operation( request=self.request, operation_content=f"删除员工 ID:{pk},员工名:{qs.staff_name}", operation_level="delete", operator=self.request.auth.name if hasattr(self.request, 'auth') and self.request.auth else None, object_id=pk, module_name="员工管理" ) except Exception as e: pass return Response(serializer.data, status=200, headers=headers) class RoleViewSet(viewsets.ModelViewSet): """角色管理API""" queryset = Role.objects.all() serializer_class = serializers.RoleSerializer pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter] ordering_fields = ['id', "name"] def get_queryset(self): return Role.objects.all() class PermissionViewSet(viewsets.ModelViewSet): """权限管理API""" queryset = Permission.objects.all() serializer_class = serializers.PermissionSerializer # pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter] ordering_fields = ['id', "page"] def get_queryset(self): role = self.request.query_params.get('role') if role: return Permission.objects.filter(role__name=role) return Permission.objects.all() class RolePermissionViewSet(viewsets.ViewSet): """角色权限配置API""" def list(self, request): """获取所有角色类型""" roles = Role.objects.values_list('name', flat=True).distinct() return Response(list(roles)) def reset_default_permissions(self, request): """恢复默认权限配置""" # 从 test_permission.py 导入配置 PAGES = [ {"primary_page": "stock", "path": "/stock/management", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "stock", "path": "/stock/stocklist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "stock", "path": "/stock/stockbinlist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "stock", "path": "/stock/emptybin", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "stock", "path": "/stock/occupiedbin", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "stock", "path": "/stock/binset", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "stock", "path": "/stock/handcount", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "erp", "path": "/erp/erpasn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "erp", "path": "/erp/erpasnmaterial", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "erp", "path": "/erp/erpdnmaterial", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "erp", "path": "/erp/erpasnaudit", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "erp", "path": "/erp/erpdn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "erp", "path": "/erp/erpsortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/asn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/predeliverystock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/preloadstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/presortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/sortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/shortage", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/more", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "inbound", "path": "/inbound/asnfinish", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "container", "path": "/container/containerlist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "container", "path": "/container/containerdetail", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "container", "path": "/container/containercategory", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "container", "path": "/container/containeroperate", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/inboundAndOutbound", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/flows_statements", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/flows", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/flows_complex", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/batchlog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/ContainerDetailLogModel", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "dashboard", "path": "/dashboard/MaterialChangeHistory", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/detaillog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/batchoperatelog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/countbatchlog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/presortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/sortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/shortage", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/containerDetail", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/batch", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "count", "path": "/count/asnfinish", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/dn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/freshorder", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/neworder", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/pickstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/pickedstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/pickinglist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/shippedstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/backorder", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/pod", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "outbound", "path": "/outbound/container_check", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodslist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodsunit", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodsclass", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodsbrand", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodscolor", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodsspecs", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodsshape", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "goods", "path": "/goods/goodsorigin", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "taskpage", "path": "/taskpage/task", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/warehouseset", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/department", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/boundcodetype", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/boundtype", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/boundbusiness", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/status", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "warehouse", "path": "/warehouse/product", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "staff", "path": "/staff/roles", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "staff", "path": "/staff/stafflist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "staff", "path": "/staff/stafflist_check_code", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "staff", "path": "/staff/stafftype", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "staff", "path": "/staff/operationlog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "uploadcenter", "path": "/uploadcenter/initializeupload", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "uploadcenter", "path": "/uploadcenter/addupload", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadinbound", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadoutbound", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadstocklist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadgoodslist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadbinlist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]}, ] ROLES = { "管理员": { "description": "系统管理员,拥有所有权限", "page_access": "all", "component_access": "all" }, "经理": { "description": "部门经理,拥有大部分管理权限", "page_access": [ "/stock/management", "/stock/stockbinlist", "/stock/stocklist", "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock", "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock", "/container/containerlist", "/container/containerdetail", "/container/containercategory", "/container/containeroperate", "/outbound/dn", "/outbound/backorder", "/outbound/container_check", "/taskpage/task", "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog", "/dashboard/flows_statements", "/dashboard/flows", "/dashboard/MaterialChangeHistory", "/dashboard/batchlog", "/dashboard/ContainerDetailLogModel", "/warehouse/department", "/warehouse/boundcodetype", "/warehouse/boundtype", "/warehouse/boundbusiness", "/warehouse/status", "/warehouse/product", "/staff/stafflist", "/staff/stafflist_check_code", "/staff/stafftype", "/staff/operationlog" ], "component_access": "all" }, "主管": { "description": "仓库主管,负责日常运营管理", "page_access": [ "/stock/management", "/stock/stockbinlist", "/stock/stocklist", "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock", "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock", "/container/containerlist", "/container/containerdetail", "/container/containercategory", "/outbound/dn", "/outbound/backorder", "/outbound/container_check", "/taskpage/task", "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog", "/dashboard/flows_statements", "/dashboard/flows", "/dashboard/MaterialChangeHistory", "/dashboard/batchlog", "/dashboard/ContainerDetailLogModel", "/staff/stafflist", "/staff/stafftype", "/staff/operationlog" ], "component_access": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"] }, "操作员": { "description": "仓库操作员,负责具体操作", "page_access": [ "/stock/management", "/stock/stockbinlist", "/stock/stocklist", "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock", "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock", "/container/containerlist", "/container/containercategory", "/outbound/dn", "/outbound/backorder", "/outbound/container_check", "/taskpage/task", "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog", "/staff/operationlog", "/dashboard/flows_statements", "/dashboard/flows" ], "component_access": ["view", "edit", "add", "download", "confirm"] }, "查看员": { "description": "数据查看员,只能查看数据", "page_access": [ "/stock/management", "/stock/stockbinlist", "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock", "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock", "/container/containerlist", "/container/containercategory", "/outbound/dn", "/outbound/backorder", "/outbound/container_check", "/taskpage/task", "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog", "/dashboard/flows_statements", "/dashboard/flows", "/staff/operationlog", ], "component_access": ["view", "download"] } } try: # 1. 创建所有权限 created_perm_count = 0 for page_info in PAGES: primary_page = page_info.get("primary_page") page_path = page_info["path"] components = page_info["components"] # 创建页面访问权限 page_permission, created = Permission.objects.get_or_create( primary_page=primary_page, page=page_path, component=None, defaults={ "name": f"{page_path} 页面访问", "description": f"访问 {page_path} 页面的权限", "enabled": True } ) if created: created_perm_count += 1 # 创建组件权限 for component in components: comp_permission, created = Permission.objects.get_or_create( primary_page=primary_page, page=page_path, component=component, defaults={ "name": f"{page_path} - {component} 组件", "description": f"在 {page_path} 页面使用 {component} 组件的权限", "enabled": True } ) if created: created_perm_count += 1 # 2. 恢复所有角色的默认权限 all_permissions = Permission.objects.all() role_count = 0 permission_count = 0 for role_name, role_config in ROLES.items(): role, created = Role.objects.get_or_create( name=role_name, defaults={"description": role_config["description"]} ) if role: role_count += 1 if role_config["page_access"] == "all": # 分配所有权限 role.permissions.set(all_permissions) permission_count += all_permissions.count() else: # 分配特定权限 assigned_permissions = [] for page_path in role_config["page_access"]: # 获取页面权限 page_perms = all_permissions.filter( page=page_path, component=None ) assigned_permissions.extend(page_perms) # 获取组件权限 comp_perms = all_permissions.filter( page=page_path, component__isnull=False ) if role_config["component_access"] == "all": assigned_permissions.extend(comp_perms) else: for perm in comp_perms: if perm.component in role_config["component_access"]: assigned_permissions.append(perm) role.permissions.set(assigned_permissions) permission_count += len(assigned_permissions) try: log_success_operation( request=request, operation_content=f"恢复默认权限配置,创建权限数:{created_perm_count},更新角色数:{role_count}", operation_level="update", operator=request.auth.name if hasattr(request, 'auth') and request.auth else None, module_name="权限管理" ) except Exception as e: pass return Response({ "message": "默认权限已恢复成功", "created_permissions": created_perm_count, "roles_updated": role_count, "permissions_assigned": permission_count }) except Exception as e: try: log_failure_operation( request=request, operation_content=f"恢复默认权限配置失败:{str(e)}", operation_level="update", operator=request.auth.name if hasattr(request, 'auth') and request.auth else None, module_name="权限管理" ) except: pass return Response({ "error": "恢复默认权限失败", "detail": str(e) }, status=500) def retrieve(self, request, pk=None): """获取特定角色的权限配置""" try: role = Role.objects.get(name=pk) serializer = serializers.RoleGETSerializer(role) return Response(serializer.data) except Role.DoesNotExist: return Response({"error": "Role not found"}, status=404) def update(self, request, pk=None): """更新角色权限(增量更新,只更新当前页面的权限)""" try: role = Role.objects.get(name=pk) permissions_data = request.data.get('permissions', []) if not permissions_data: return Response({"message": "No permissions to update"}, status=400) # 获取要更新的页面(从第一个权限中获取,因为所有权限应该属于同一页面) target_page = permissions_data[0].get('page') if permissions_data else None if not target_page: return Response({"error": "Page is required"}, status=400) # 获取当前页面已关联的权限,从角色中移除 existing_perms = role.permissions.filter(page=target_page) # 从角色的权限中移除当前页面的所有权限 role.permissions.remove(*existing_perms) # 添加/更新当前页面的权限 # 注意:这里只控制角色与权限的关联关系,不修改权限对象本身的 enabled 状态 new_perms = [] for perm_data in permissions_data: # 确保所有权限都属于同一页面 if perm_data.get('page') != target_page: continue # 如果 enabled 为 False,表示该角色不应该拥有此权限,跳过不添加 if not perm_data.get('enabled', True): continue component = perm_data.get('component') # 处理 component 为 null 的情况 if component is None or component == '': component = None # 获取或创建权限对象(不修改 enabled 状态) perm, created = Permission.objects.get_or_create( page=perm_data['page'], component=component, defaults={ 'name': perm_data.get('name', f"{perm_data['page']}-{perm_data.get('component', 'page')}"), 'enabled': True, # 新创建的权限默认启用 'primary_page': perm_data.get('primary_page', ''), 'description': perm_data.get('description', '') } ) # 不修改已存在权限的 enabled 状态,保持原有状态 # 只将 enabled 为 True 的权限添加到角色 new_perms.append(perm) # 批量添加新权限到角色(只添加 enabled 为 True 的权限) if new_perms: role.permissions.add(*new_perms) try: log_success_operation( request=request, operation_content=f"更新角色权限:{pk},页面:{target_page}", operation_level="update", operator=request.auth.name if hasattr(request, 'auth') and request.auth else None, module_name="权限管理" ) except Exception as e: pass return Response({"message": "Permissions updated successfully"}) except Role.DoesNotExist: return Response({"error": "Role not found"}, status=404) class RolePagePermissionViewSet(viewsets.ViewSet): """角色权限配置API""" def list(self, request): """获取所有角色类型""" roles = Role.objects.values_list('name', flat=True).distinct() return Response(list(roles)) def retrieve(self, request, pk=None): """获取特定角色的权限配置""" try: role = Role.objects.get(name=pk) serializer = serializers.RolePageGETSerializer(role) return Response(serializer.data) except Role.DoesNotExist: return Response({"error": "Role not found"}, status=404) def get_page_permissions(self, request, pk=None): """获取特定角色的页面访问权限配置""" try: role = Role.objects.get(name=pk) primary_page = request.data.get('primary_page') if primary_page: fliterpermissions= role.permissions.filter(primary_page=primary_page) serializer = self.get_permissions_group(fliterpermissions) return Response(serializer) serializer = self.get_permissions_group(role.permissions.all()) return Response(serializer) except Role.DoesNotExist: return Response({"error": "Role not found"}, status=404) def get_permissions_group(self, permissions): # 获取角色关联的所有权限并预取数据 # 按page字段分组,只处理component为null的权限 page_access = {} for perm in permissions: # 只处理页面访问权限(component为null) if perm.component is None: page_access[perm.page] = perm.enabled # 转换为前端需要的格式 return [{"page": page, "enabled": enabled} for page, enabled in page_access.items()] class RolePageComponentPermissionViewSet(viewsets.ViewSet): """角色权限配置API""" def get_page_component_permissions(self, request, pk=None): """获取特定角色的页面访问权限配置""" try: role = Role.objects.get(name=pk) page = request.data.get('page') if page: fliterpermissions= role.permissions.filter(page=page) serializer = self.get_permissions_group(fliterpermissions) return Response(serializer) serializer = self.get_permissions_group(role.permissions.all()) return Response(serializer) except Role.DoesNotExist: return Response({"error": "Role not found"}, status=404) def get_permissions_group(self, permissions): # 获取角色关联的所有权限并预取数据 page_access = {} for perm in permissions: if perm.component is not None: page_access[perm.component] = perm.enabled # 转换为前端需要的格式 return [{"component": component, "enabled": enabled} for component, enabled in page_access.items()] class TypeAPIViewSet(viewsets.ModelViewSet): """ list: Response a data list(all) """ pagination_class = MyPageNumberPagination filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "create_time", "update_time", ] filter_class = TypeFilter def get_queryset(self): if self.request.user: return TypeListModel.objects.filter(openid='init_data') else: return TypeListModel.objects.none() def get_serializer_class(self): if self.action in ['list']: return serializers.StaffTypeGetSerializer else: return self.http_method_not_allowed(request=self.request) class FileDownloadView(viewsets.ModelViewSet): renderer_classes = (FileRenderCN,) + tuple(api_settings.DEFAULT_RENDERER_CLASSES) filter_backends = [DjangoFilterBackend, OrderingFilter, ] ordering_fields = ['id', "create_time", "update_time", ] filter_class = Filter def get_project(self): try: id = self.kwargs.get('pk') return id except: return None def get_queryset(self): id = self.get_project() if self.request.user: if id is None: return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False) else: return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False) else: return ListModel.objects.none() def get_serializer_class(self): if self.action in ['list']: return serializers.FileRenderSerializer else: return self.http_method_not_allowed(request=self.request) def get_lang(self, data): lang = self.request.META.get('HTTP_LANGUAGE') if lang: if lang == 'zh-hans': return FileRenderCN().render(data) else: return FileRenderEN().render(data) else: return FileRenderEN().render(data) def list(self, request, *args, **kwargs): from datetime import datetime dt = datetime.now() data = ( FileRenderSerializer(instance).data for instance in self.filter_queryset(self.get_queryset()) ) renderer = self.get_lang(data) response = StreamingHttpResponse( renderer, content_type="text/csv" ) response['Content-Disposition'] = "attachment; filename='staff_{}.csv'".format( str(dt.strftime('%Y%m%d%H%M%S%f'))) try: log_success_operation( request=request, operation_content="下载员工列表文件", operation_level="download", operator=request.auth.name if hasattr(request, 'auth') and request.auth else None, module_name="员工管理" ) except Exception as e: pass return response