|
|
@@ -1,4 +1,5 @@
|
|
|
from rest_framework import viewsets
|
|
|
+from rest_framework.decorators import action
|
|
|
from .models import ListModel, TypeListModel
|
|
|
from . import serializers
|
|
|
from utils.page import MyPageNumberPagination
|
|
|
@@ -140,17 +141,32 @@ class APIViewSet(viewsets.ModelViewSet):
|
|
|
check_code = random.randint(1000, 9999)
|
|
|
data['check_code'] = check_code
|
|
|
|
|
|
- # 创建用户
|
|
|
- user = User.objects.create_user(
|
|
|
- username=str(data['staff_name']),
|
|
|
- password=str(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.objects.create(user_id=user.id, name=str(data['name']),
|
|
|
- openid=app_code, appid=app_code,
|
|
|
- t_code=Md5.md5(str(timezone.now())),
|
|
|
- developer=1, ip=ip)
|
|
|
+ # 创建/更新用户档案
|
|
|
+ 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)
|
|
|
@@ -172,12 +188,77 @@ class APIViewSet(viewsets.ModelViewSet):
|
|
|
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)
|
|
|
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))
|
|
|
+
|
|
|
+ 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:
|
|
|
@@ -191,9 +272,27 @@ class APIViewSet(viewsets.ModelViewSet):
|
|
|
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)
|
|
|
return Response(serializer.data, status=200, headers=headers)
|
|
|
|
|
|
@@ -241,6 +340,252 @@ class RolePermissionViewSet(viewsets.ViewSet):
|
|
|
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": "/permission/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": "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"
|
|
|
+ ],
|
|
|
+ "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"
|
|
|
+ ],
|
|
|
+ "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",
|
|
|
+ "/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"
|
|
|
+ ],
|
|
|
+ "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)
|
|
|
+
|
|
|
+ return Response({
|
|
|
+ "message": "默认权限已恢复成功",
|
|
|
+ "created_permissions": created_perm_count,
|
|
|
+ "roles_updated": role_count,
|
|
|
+ "permissions_assigned": permission_count
|
|
|
+ })
|
|
|
+ except Exception as e:
|
|
|
+ return Response({
|
|
|
+ "error": "恢复默认权限失败",
|
|
|
+ "detail": str(e)
|
|
|
+ }, status=500)
|
|
|
+
|
|
|
def retrieve(self, request, pk=None):
|
|
|
"""获取特定角色的权限配置"""
|
|
|
try:
|
|
|
@@ -252,22 +597,61 @@ class RolePermissionViewSet(viewsets.ViewSet):
|
|
|
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', [])
|
|
|
|
|
|
- # 清除现有权限
|
|
|
- role.permissions.clear()
|
|
|
+ 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=perm_data.get('component'),
|
|
|
- defaults={'enabled': perm_data['enabled']}
|
|
|
+ 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', '')
|
|
|
+ }
|
|
|
)
|
|
|
- role.permissions.add(perm)
|
|
|
+
|
|
|
+ # 不修改已存在权限的 enabled 状态,保持原有状态
|
|
|
+
|
|
|
+ # 只将 enabled 为 True 的权限添加到角色
|
|
|
+ new_perms.append(perm)
|
|
|
+
|
|
|
+ # 批量添加新权限到角色(只添加 enabled 为 True 的权限)
|
|
|
+ if new_perms:
|
|
|
+ role.permissions.add(*new_perms)
|
|
|
|
|
|
return Response({"message": "Permissions updated successfully"})
|
|
|
except Role.DoesNotExist:
|