views.py 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. from rest_framework import viewsets
  2. from rest_framework.decorators import action
  3. from .models import ListModel, TypeListModel
  4. from . import serializers
  5. from utils.page import MyPageNumberPagination
  6. from rest_framework.filters import OrderingFilter
  7. from django_filters.rest_framework import DjangoFilterBackend
  8. from rest_framework.response import Response
  9. from .filter import Filter, TypeFilter
  10. from rest_framework.exceptions import APIException
  11. from .serializers import FileRenderSerializer
  12. from django.http import StreamingHttpResponse
  13. from .files import FileRenderCN, FileRenderEN
  14. from rest_framework.settings import api_settings
  15. from rest_framework import permissions
  16. from staff.models import ListModel as staff
  17. from userprofile.models import Users
  18. from django.utils import timezone
  19. from utils.md5 import Md5
  20. import random
  21. from django.contrib.auth.models import User
  22. from .models import Role, Permission # 新增角色和权限模型导入
  23. class APIViewSet(viewsets.ModelViewSet):
  24. """
  25. retrieve:
  26. Response a data list(get)
  27. list:
  28. Response a data list(all)
  29. create:
  30. Create a data line(post)
  31. delete:
  32. Delete a data line(delete)
  33. partial_update:
  34. Partial_update a data(patch:partial_update)
  35. update:
  36. Update a data(put:update)
  37. """
  38. pagination_class = MyPageNumberPagination
  39. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  40. ordering_fields = ['id', "create_time", "update_time", ]
  41. filter_class = Filter
  42. def list(self, request, *args, **kwargs):
  43. # staff_name = str(request.GET.get('staff_name'))
  44. # check_code = request.GET.get('check_code')
  45. # if staff_name == None and check_code == None:
  46. # return super().list(request, *args, **kwargs)
  47. # elif staff_name != None and check_code == None:
  48. # return super().list(request, *args, **kwargs)
  49. # else:
  50. # staff_name_obj = ListModel.objects.filter(openid=self.request.auth.openid, staff_name=staff_name,
  51. # is_delete=False).first()
  52. # if staff_name_obj is None:
  53. # raise APIException({"detail": "用户名不存在"})
  54. # elif staff_name_obj.is_lock is True:
  55. # raise APIException({"detail": "用户已被锁定,请联系管理员"})
  56. # elif staff_name_obj.error_check_code_counter == 3:
  57. # staff_name_obj.is_lock = True
  58. # staff_name_obj.error_check_code_counter = 0
  59. # staff_name_obj.save()
  60. # raise APIException({"detail": "用户已被锁定,请联系管理员"})
  61. # if type(check_code) == str:
  62. # check_code = int(check_code)
  63. # if check_code != None:
  64. # if staff_name_obj.check_code != check_code:
  65. # staff_name_obj.error_check_code_counter = int(staff_name_obj.error_check_code_counter) + 1
  66. # staff_name_obj.save()
  67. # raise APIException({"detail": "验证码错误"})
  68. # else:
  69. # staff_name_obj.error_check_code_counter = 0
  70. # staff_name_obj.save()
  71. # return super().list(request, *args, **kwargs)
  72. # else:
  73. return super().list(request, *args, **kwargs)
  74. def get_project(self):
  75. try:
  76. id = self.kwargs.get('pk')
  77. return id
  78. except:
  79. return None
  80. def get_queryset(self):
  81. id = self.get_project()
  82. if self.request.user:
  83. if id is None:
  84. return ListModel.objects.filter(is_delete=False)
  85. else:
  86. return ListModel.objects.filter(id=id, is_delete=False)
  87. else:
  88. return ListModel.objects.none()
  89. def get_serializer_class(self):
  90. staff_type = ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False).first().staff_type
  91. if staff_type not in ['admin', '主管', '管理员','经理']:
  92. if self.action in ['list', 'retrieve', 'destroy']:
  93. return serializers.userStaffGetSerializer
  94. elif self.action in ['create']:
  95. return serializers.userStaffPostSerializer
  96. elif self.action in ['update']:
  97. return serializers.userStaffUpdateSerializer
  98. elif self.action in ['partial_update']:
  99. return serializers.userStaffPartialUpdateSerializer
  100. else:
  101. return self.http_method_not_allowed(request=self.request)
  102. else:
  103. if self.action in ['list', 'retrieve', 'destroy']:
  104. return serializers.StaffGetSerializer
  105. elif self.action in ['create']:
  106. return serializers.StaffPostSerializer
  107. elif self.action in ['update']:
  108. return serializers.StaffUpdateSerializer
  109. elif self.action in ['partial_update']:
  110. return serializers.StaffPartialUpdateSerializer
  111. else:
  112. return self.http_method_not_allowed(request=self.request)
  113. def create(self, request, *args, **kwargs):
  114. data = self.request.data
  115. data['openid'] = self.request.auth.openid
  116. # 检查角色是否存在
  117. role_name = data.get('role')
  118. if role_name:
  119. role, created = Role.objects.get_or_create(name=role_name)
  120. data['role'] = role.id
  121. if ListModel.objects.filter(openid=data['openid'], staff_name=data['staff_name'], is_delete=False).exists():
  122. raise APIException({"detail": "Data exists"})
  123. else:
  124. app_code = Md5.md5(data['staff_name'] + '1')
  125. data['appid'] = app_code
  126. check_code = random.randint(1000, 9999)
  127. data['check_code'] = check_code
  128. # 创建/更新 Django auth 用户
  129. username = str(data['staff_name'])
  130. password = str(check_code)
  131. try:
  132. user = User.objects.get(username=username)
  133. user.set_password(password)
  134. user.save()
  135. except User.DoesNotExist:
  136. user = User.objects.create_user(username=username, password=password)
  137. ip = request.META.get('HTTP_X_FORWARDED_FOR') if request.META.get(
  138. 'HTTP_X_FORWARDED_FOR') else request.META.get('REMOTE_ADDR')
  139. # 创建/更新用户档案
  140. users_defaults = dict(
  141. name=str(data['staff_name']),
  142. openid=app_code, appid=app_code,
  143. t_code=Md5.md5(str(timezone.now())),
  144. developer=1, ip=ip
  145. )
  146. profile, created = Users.objects.get_or_create(user_id=user.id, defaults=users_defaults)
  147. if not created:
  148. # 同步基础字段
  149. profile.name = users_defaults['name']
  150. profile.openid = users_defaults['openid']
  151. profile.appid = users_defaults['appid']
  152. profile.ip = users_defaults['ip']
  153. profile.save()
  154. serializer = self.get_serializer(data=data)
  155. serializer.is_valid(raise_exception=True)
  156. serializer.save()
  157. headers = self.get_success_headers(serializer.data)
  158. return Response(serializer.data, status=200, headers=headers)
  159. def update(self, request, pk):
  160. qs = self.get_object()
  161. # if qs.openid != self.request.auth.openid:
  162. # creator = ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
  163. # raise APIException({"detail": "该用户不是您创建的,不能修改"})
  164. # else:
  165. data = self.request.data
  166. # 更新角色
  167. role_name = data.get('role')
  168. if role_name:
  169. role, created = Role.objects.get_or_create(name=role_name)
  170. data['role'] = role.id
  171. old_staff_name = qs.staff_name
  172. serializer = self.get_serializer(qs, data=data)
  173. serializer.is_valid(raise_exception=True)
  174. serializer.save()
  175. # 同步 Django auth 用户与用户档案
  176. new_staff_name = serializer.instance.staff_name
  177. if old_staff_name != new_staff_name:
  178. try:
  179. auth_user = User.objects.get(username=str(old_staff_name))
  180. auth_user.username = str(new_staff_name)
  181. auth_user.save()
  182. # 同步 userprofile.Users
  183. profile = Users.objects.filter(user_id=auth_user.id).first()
  184. if profile:
  185. profile.name = str(new_staff_name)
  186. profile.save()
  187. except User.DoesNotExist:
  188. # 如不存在旧账号,则以新名称创建
  189. auth_user = User.objects.create_user(username=str(new_staff_name), password=str(qs.check_code))
  190. Users.objects.get_or_create(user_id=auth_user.id, defaults=dict(
  191. name=str(new_staff_name), openid=qs.appid, appid=qs.appid,
  192. t_code=Md5.md5(str(timezone.now())), developer=1, ip=request.META.get('REMOTE_ADDR')
  193. ))
  194. headers = self.get_success_headers(serializer.data)
  195. return Response(serializer.data, status=200, headers=headers)
  196. def change_check_code(self, request, pk=None):
  197. """
  198. 修改员工验证码(密码)
  199. - 普通员工:只能修改自己的验证码
  200. - 管理/主管/管理员/经理:可修改任意员工验证码
  201. 请求体: { "new_check_code": 1234 }
  202. """
  203. target_staff = self.get_object()
  204. current_staff = ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False).first()
  205. if current_staff is None:
  206. raise APIException({"detail": "当前用户不存在或未登录"})
  207. privileged_types = ['admin', '主管', '管理员', '经理']
  208. can_modify_any = str(current_staff.staff_type) in privileged_types
  209. if not can_modify_any and current_staff.id != target_staff.id:
  210. raise APIException({"detail": "无权限修改他人验证码"})
  211. new_code = request.data.get('new_check_code')
  212. try:
  213. new_code_int = int(new_code)
  214. except (TypeError, ValueError):
  215. raise APIException({"detail": "new_check_code 必须为4位数字"})
  216. if new_code_int < 0 or new_code_int > 9999:
  217. raise APIException({"detail": "new_check_code 必须为0-9999之间的数字"})
  218. # 更新staff表
  219. target_staff.check_code = new_code_int
  220. target_staff.error_check_code_counter = 0
  221. target_staff.is_lock = False
  222. target_staff.save()
  223. # 同步更新Django auth用户密码(用户名为staff_name)
  224. try:
  225. auth_user = User.objects.get(username=str(target_staff.staff_name))
  226. auth_user.set_password(str(new_code_int))
  227. auth_user.save()
  228. except User.DoesNotExist:
  229. # 若不存在则创建,保持行为幂等
  230. User.objects.create_user(username=str(target_staff.staff_name), password=str(new_code_int))
  231. return Response({"message": "验证码已更新", "id": target_staff.id, "check_code": target_staff.check_code})
  232. def partial_update(self, request, pk):
  233. qs = self.get_object()
  234. if qs.openid != self.request.auth.openid:
  235. raise APIException({"detail": "Cannot Update Data Which Not Yours"})
  236. else:
  237. data = self.request.data
  238. # 更新角色
  239. role_name = data.get('role')
  240. if role_name:
  241. role, created = Role.objects.get_or_create(name=role_name)
  242. data['role'] = role.id
  243. old_staff_name = qs.staff_name
  244. serializer = self.get_serializer(qs, data=data, partial=True)
  245. serializer.is_valid(raise_exception=True)
  246. serializer.save()
  247. # 同步 Django auth 用户与用户档案
  248. new_staff_name = serializer.instance.staff_name
  249. if old_staff_name != new_staff_name:
  250. try:
  251. auth_user = User.objects.get(username=str(old_staff_name))
  252. auth_user.username = str(new_staff_name)
  253. auth_user.save()
  254. profile = Users.objects.filter(user_id=auth_user.id).first()
  255. if profile:
  256. profile.name = str(new_staff_name)
  257. profile.save()
  258. except User.DoesNotExist:
  259. auth_user = User.objects.create_user(username=str(new_staff_name), password=str(qs.check_code))
  260. Users.objects.get_or_create(user_id=auth_user.id, defaults=dict(
  261. name=str(new_staff_name), openid=qs.appid, appid=qs.appid,
  262. t_code=Md5.md5(str(timezone.now())), developer=1, ip=request.META.get('REMOTE_ADDR')
  263. ))
  264. headers = self.get_success_headers(serializer.data)
  265. return Response(serializer.data, status=200, headers=headers)
  266. def destroy(self, request, pk):
  267. qs = self.get_object()
  268. if qs.openid != self.request.auth.openid:
  269. raise APIException({"detail": "Cannot Delete Data Which Not Yours"})
  270. else:
  271. qs.is_delete = True
  272. qs.save()
  273. serializer = self.get_serializer(qs, many=False)
  274. headers = self.get_success_headers(serializer.data)
  275. return Response(serializer.data, status=200, headers=headers)
  276. class RoleViewSet(viewsets.ModelViewSet):
  277. """角色管理API"""
  278. queryset = Role.objects.all()
  279. serializer_class = serializers.RoleSerializer
  280. pagination_class = MyPageNumberPagination
  281. filter_backends = [DjangoFilterBackend, OrderingFilter]
  282. ordering_fields = ['id', "name"]
  283. def get_queryset(self):
  284. return Role.objects.all()
  285. class PermissionViewSet(viewsets.ModelViewSet):
  286. """权限管理API"""
  287. queryset = Permission.objects.all()
  288. serializer_class = serializers.PermissionSerializer
  289. # pagination_class = MyPageNumberPagination
  290. filter_backends = [DjangoFilterBackend, OrderingFilter]
  291. ordering_fields = ['id', "page"]
  292. def get_queryset(self):
  293. role = self.request.query_params.get('role')
  294. if role:
  295. return Permission.objects.filter(role__name=role)
  296. return Permission.objects.all()
  297. class RolePermissionViewSet(viewsets.ViewSet):
  298. """角色权限配置API"""
  299. def list(self, request):
  300. """获取所有角色类型"""
  301. roles = Role.objects.values_list('name', flat=True).distinct()
  302. return Response(list(roles))
  303. def reset_default_permissions(self, request):
  304. """恢复默认权限配置"""
  305. # 从 test_permission.py 导入配置
  306. PAGES = [
  307. {"primary_page": "stock", "path": "/stock/management", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  308. {"primary_page": "stock", "path": "/stock/stocklist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  309. {"primary_page": "stock", "path": "/stock/stockbinlist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  310. {"primary_page": "stock", "path": "/stock/emptybin", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  311. {"primary_page": "stock", "path": "/stock/occupiedbin", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  312. {"primary_page": "stock", "path": "/stock/binset", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  313. {"primary_page": "stock", "path": "/stock/handcount", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  314. {"primary_page": "erp", "path": "/erp/erpasn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  315. {"primary_page": "erp", "path": "/erp/erpasnmaterial", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  316. {"primary_page": "erp", "path": "/erp/erpdnmaterial", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  317. {"primary_page": "erp", "path": "/erp/erpasnaudit", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  318. {"primary_page": "erp", "path": "/erp/erpdn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  319. {"primary_page": "erp", "path": "/erp/erpsortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  320. {"primary_page": "inbound", "path": "/inbound/asn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  321. {"primary_page": "inbound", "path": "/inbound/predeliverystock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  322. {"primary_page": "inbound", "path": "/inbound/preloadstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  323. {"primary_page": "inbound", "path": "/inbound/presortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  324. {"primary_page": "inbound", "path": "/inbound/sortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  325. {"primary_page": "inbound", "path": "/inbound/shortage", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  326. {"primary_page": "inbound", "path": "/inbound/more", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  327. {"primary_page": "inbound", "path": "/inbound/asnfinish", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  328. {"primary_page": "container", "path": "/container/containerlist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  329. {"primary_page": "container", "path": "/container/containerdetail", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  330. {"primary_page": "container", "path": "/container/containercategory", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  331. {"primary_page": "container", "path": "/container/containeroperate", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  332. {"primary_page": "dashboard", "path": "/dashboard/inboundAndOutbound", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  333. {"primary_page": "dashboard", "path": "/dashboard/flows_statements", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  334. {"primary_page": "dashboard", "path": "/dashboard/flows", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  335. {"primary_page": "dashboard", "path": "/dashboard/flows_complex", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  336. {"primary_page": "dashboard", "path": "/dashboard/batchlog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  337. {"primary_page": "dashboard", "path": "/dashboard/ContainerDetailLogModel", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  338. {"primary_page": "dashboard", "path": "/dashboard/MaterialChangeHistory", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  339. {"primary_page": "count", "path": "/count/detaillog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  340. {"primary_page": "count", "path": "/count/batchoperatelog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  341. {"primary_page": "count", "path": "/count/countbatchlog", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  342. {"primary_page": "count", "path": "/count/presortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  343. {"primary_page": "count", "path": "/count/sortstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  344. {"primary_page": "count", "path": "/count/shortage", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  345. {"primary_page": "count", "path": "/count/containerDetail", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  346. {"primary_page": "count", "path": "/count/batch", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  347. {"primary_page": "count", "path": "/count/asnfinish", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  348. {"primary_page": "outbound", "path": "/outbound/dn", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  349. {"primary_page": "outbound", "path": "/outbound/freshorder", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  350. {"primary_page": "outbound", "path": "/outbound/neworder", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  351. {"primary_page": "outbound", "path": "/outbound/pickstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  352. {"primary_page": "outbound", "path": "/outbound/pickedstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  353. {"primary_page": "outbound", "path": "/outbound/pickinglist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  354. {"primary_page": "outbound", "path": "/outbound/shippedstock", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  355. {"primary_page": "outbound", "path": "/outbound/backorder", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  356. {"primary_page": "outbound", "path": "/outbound/pod", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  357. {"primary_page": "outbound", "path": "/outbound/container_check", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  358. {"primary_page": "goods", "path": "/goods/goodslist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  359. {"primary_page": "goods", "path": "/goods/goodsunit", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  360. {"primary_page": "goods", "path": "/goods/goodsclass", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  361. {"primary_page": "goods", "path": "/goods/goodsbrand", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  362. {"primary_page": "goods", "path": "/goods/goodscolor", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  363. {"primary_page": "goods", "path": "/goods/goodsspecs", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  364. {"primary_page": "goods", "path": "/goods/goodsshape", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  365. {"primary_page": "goods", "path": "/goods/goodsorigin", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  366. {"primary_page": "taskpage", "path": "/taskpage/task", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  367. {"primary_page": "warehouse", "path": "/warehouse/warehouseset", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  368. {"primary_page": "warehouse", "path": "/warehouse/department", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  369. {"primary_page": "warehouse", "path": "/warehouse/boundcodetype", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  370. {"primary_page": "warehouse", "path": "/warehouse/boundtype", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  371. {"primary_page": "warehouse", "path": "/warehouse/boundbusiness", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  372. {"primary_page": "warehouse", "path": "/warehouse/status", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  373. {"primary_page": "warehouse", "path": "/warehouse/product", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  374. {"primary_page": "staff", "path": "/permission/roles", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  375. {"primary_page": "staff", "path": "/staff/stafflist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  376. {"primary_page": "staff", "path": "/staff/stafflist_check_code", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  377. {"primary_page": "staff", "path": "/staff/stafftype", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  378. {"primary_page": "uploadcenter", "path": "/uploadcenter/initializeupload", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  379. {"primary_page": "uploadcenter", "path": "/uploadcenter/addupload", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  380. {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadinbound", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  381. {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadoutbound", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  382. {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadstocklist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  383. {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadgoodslist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  384. {"primary_page": "downloadcenter", "path": "/downloadcenter/downloadbinlist", "components": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]},
  385. ]
  386. ROLES = {
  387. "管理员": {
  388. "description": "系统管理员,拥有所有权限",
  389. "page_access": "all",
  390. "component_access": "all"
  391. },
  392. "经理": {
  393. "description": "部门经理,拥有大部分管理权限",
  394. "page_access": [
  395. "/stock/management", "/stock/stockbinlist", "/stock/stocklist",
  396. "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock",
  397. "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock",
  398. "/container/containerlist", "/container/containerdetail", "/container/containercategory", "/container/containeroperate",
  399. "/outbound/dn", "/outbound/backorder", "/outbound/container_check",
  400. "/taskpage/task",
  401. "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog",
  402. "/dashboard/flows_statements", "/dashboard/flows", "/dashboard/MaterialChangeHistory", "/dashboard/batchlog", "/dashboard/ContainerDetailLogModel",
  403. "/warehouse/department", "/warehouse/boundcodetype", "/warehouse/boundtype", "/warehouse/boundbusiness", "/warehouse/status", "/warehouse/product",
  404. "/staff/stafflist", "/staff/stafflist_check_code", "/staff/stafftype"
  405. ],
  406. "component_access": "all"
  407. },
  408. "主管": {
  409. "description": "仓库主管,负责日常运营管理",
  410. "page_access": [
  411. "/stock/management", "/stock/stockbinlist", "/stock/stocklist",
  412. "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock",
  413. "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock",
  414. "/container/containerlist", "/container/containerdetail", "/container/containercategory",
  415. "/outbound/dn", "/outbound/backorder", "/outbound/container_check",
  416. "/taskpage/task",
  417. "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog",
  418. "/dashboard/flows_statements", "/dashboard/flows", "/dashboard/MaterialChangeHistory", "/dashboard/batchlog", "/dashboard/ContainerDetailLogModel",
  419. "/staff/stafflist", "/staff/stafftype"
  420. ],
  421. "component_access": ["view", "edit", "add", "delete", "export", "confirm", "adjust", "download"]
  422. },
  423. "操作员": {
  424. "description": "仓库操作员,负责具体操作",
  425. "page_access": [
  426. "/stock/management", "/stock/stockbinlist", "/stock/stocklist",
  427. "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock",
  428. "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock",
  429. "/container/containerlist", "/container/containercategory",
  430. "/outbound/dn", "/outbound/backorder", "/outbound/container_check",
  431. "/taskpage/task",
  432. "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog",
  433. "/dashboard/flows_statements", "/dashboard/flows"
  434. ],
  435. "component_access": ["view", "edit", "add", "download", "confirm"]
  436. },
  437. "查看员": {
  438. "description": "数据查看员,只能查看数据",
  439. "page_access": [
  440. "/stock/management", "/stock/stockbinlist",
  441. "/erp/erpasn", "/erp/erpasnmaterial", "/erp/erpdnmaterial", "/erp/erpdn", "/erp/erpsortstock",
  442. "/inbound/asn", "/inbound/predeliverystock", "/inbound/sortstock",
  443. "/container/containerlist", "/container/containercategory",
  444. "/outbound/dn", "/outbound/backorder", "/outbound/container_check",
  445. "/taskpage/task",
  446. "/count/batch", "/count/countbatchlog", "/count/detaillog", "/count/batchoperatelog",
  447. "/dashboard/flows_statements", "/dashboard/flows"
  448. ],
  449. "component_access": ["view", "download"]
  450. }
  451. }
  452. try:
  453. # 1. 创建所有权限
  454. created_perm_count = 0
  455. for page_info in PAGES:
  456. primary_page = page_info.get("primary_page")
  457. page_path = page_info["path"]
  458. components = page_info["components"]
  459. # 创建页面访问权限
  460. page_permission, created = Permission.objects.get_or_create(
  461. primary_page=primary_page,
  462. page=page_path,
  463. component=None,
  464. defaults={
  465. "name": f"{page_path} 页面访问",
  466. "description": f"访问 {page_path} 页面的权限",
  467. "enabled": True
  468. }
  469. )
  470. if created:
  471. created_perm_count += 1
  472. # 创建组件权限
  473. for component in components:
  474. comp_permission, created = Permission.objects.get_or_create(
  475. primary_page=primary_page,
  476. page=page_path,
  477. component=component,
  478. defaults={
  479. "name": f"{page_path} - {component} 组件",
  480. "description": f"在 {page_path} 页面使用 {component} 组件的权限",
  481. "enabled": True
  482. }
  483. )
  484. if created:
  485. created_perm_count += 1
  486. # 2. 恢复所有角色的默认权限
  487. all_permissions = Permission.objects.all()
  488. role_count = 0
  489. permission_count = 0
  490. for role_name, role_config in ROLES.items():
  491. role, created = Role.objects.get_or_create(
  492. name=role_name,
  493. defaults={"description": role_config["description"]}
  494. )
  495. if role:
  496. role_count += 1
  497. if role_config["page_access"] == "all":
  498. # 分配所有权限
  499. role.permissions.set(all_permissions)
  500. permission_count += all_permissions.count()
  501. else:
  502. # 分配特定权限
  503. assigned_permissions = []
  504. for page_path in role_config["page_access"]:
  505. # 获取页面权限
  506. page_perms = all_permissions.filter(
  507. page=page_path,
  508. component=None
  509. )
  510. assigned_permissions.extend(page_perms)
  511. # 获取组件权限
  512. comp_perms = all_permissions.filter(
  513. page=page_path,
  514. component__isnull=False
  515. )
  516. if role_config["component_access"] == "all":
  517. assigned_permissions.extend(comp_perms)
  518. else:
  519. for perm in comp_perms:
  520. if perm.component in role_config["component_access"]:
  521. assigned_permissions.append(perm)
  522. role.permissions.set(assigned_permissions)
  523. permission_count += len(assigned_permissions)
  524. return Response({
  525. "message": "默认权限已恢复成功",
  526. "created_permissions": created_perm_count,
  527. "roles_updated": role_count,
  528. "permissions_assigned": permission_count
  529. })
  530. except Exception as e:
  531. return Response({
  532. "error": "恢复默认权限失败",
  533. "detail": str(e)
  534. }, status=500)
  535. def retrieve(self, request, pk=None):
  536. """获取特定角色的权限配置"""
  537. try:
  538. role = Role.objects.get(name=pk)
  539. serializer = serializers.RoleGETSerializer(role)
  540. return Response(serializer.data)
  541. except Role.DoesNotExist:
  542. return Response({"error": "Role not found"}, status=404)
  543. def update(self, request, pk=None):
  544. """更新角色权限(增量更新,只更新当前页面的权限)"""
  545. try:
  546. role = Role.objects.get(name=pk)
  547. permissions_data = request.data.get('permissions', [])
  548. if not permissions_data:
  549. return Response({"message": "No permissions to update"}, status=400)
  550. # 获取要更新的页面(从第一个权限中获取,因为所有权限应该属于同一页面)
  551. target_page = permissions_data[0].get('page') if permissions_data else None
  552. if not target_page:
  553. return Response({"error": "Page is required"}, status=400)
  554. # 获取当前页面已关联的权限,从角色中移除
  555. existing_perms = role.permissions.filter(page=target_page)
  556. # 从角色的权限中移除当前页面的所有权限
  557. role.permissions.remove(*existing_perms)
  558. # 添加/更新当前页面的权限
  559. # 注意:这里只控制角色与权限的关联关系,不修改权限对象本身的 enabled 状态
  560. new_perms = []
  561. for perm_data in permissions_data:
  562. # 确保所有权限都属于同一页面
  563. if perm_data.get('page') != target_page:
  564. continue
  565. # 如果 enabled 为 False,表示该角色不应该拥有此权限,跳过不添加
  566. if not perm_data.get('enabled', True):
  567. continue
  568. component = perm_data.get('component')
  569. # 处理 component 为 null 的情况
  570. if component is None or component == '':
  571. component = None
  572. # 获取或创建权限对象(不修改 enabled 状态)
  573. perm, created = Permission.objects.get_or_create(
  574. page=perm_data['page'],
  575. component=component,
  576. defaults={
  577. 'name': perm_data.get('name', f"{perm_data['page']}-{perm_data.get('component', 'page')}"),
  578. 'enabled': True, # 新创建的权限默认启用
  579. 'primary_page': perm_data.get('primary_page', ''),
  580. 'description': perm_data.get('description', '')
  581. }
  582. )
  583. # 不修改已存在权限的 enabled 状态,保持原有状态
  584. # 只将 enabled 为 True 的权限添加到角色
  585. new_perms.append(perm)
  586. # 批量添加新权限到角色(只添加 enabled 为 True 的权限)
  587. if new_perms:
  588. role.permissions.add(*new_perms)
  589. return Response({"message": "Permissions updated successfully"})
  590. except Role.DoesNotExist:
  591. return Response({"error": "Role not found"}, status=404)
  592. class RolePagePermissionViewSet(viewsets.ViewSet):
  593. """角色权限配置API"""
  594. def list(self, request):
  595. """获取所有角色类型"""
  596. roles = Role.objects.values_list('name', flat=True).distinct()
  597. return Response(list(roles))
  598. def retrieve(self, request, pk=None):
  599. """获取特定角色的权限配置"""
  600. try:
  601. role = Role.objects.get(name=pk)
  602. serializer = serializers.RolePageGETSerializer(role)
  603. return Response(serializer.data)
  604. except Role.DoesNotExist:
  605. return Response({"error": "Role not found"}, status=404)
  606. def get_page_permissions(self, request, pk=None):
  607. """获取特定角色的页面访问权限配置"""
  608. try:
  609. role = Role.objects.get(name=pk)
  610. primary_page = request.data.get('primary_page')
  611. if primary_page:
  612. fliterpermissions= role.permissions.filter(primary_page=primary_page)
  613. serializer = self.get_permissions_group(fliterpermissions)
  614. return Response(serializer)
  615. serializer = self.get_permissions_group(role.permissions.all())
  616. return Response(serializer)
  617. except Role.DoesNotExist:
  618. return Response({"error": "Role not found"}, status=404)
  619. def get_permissions_group(self, permissions):
  620. # 获取角色关联的所有权限并预取数据
  621. # 按page字段分组,只处理component为null的权限
  622. page_access = {}
  623. for perm in permissions:
  624. # 只处理页面访问权限(component为null)
  625. if perm.component is None:
  626. page_access[perm.page] = perm.enabled
  627. # 转换为前端需要的格式
  628. return [{"page": page, "enabled": enabled} for page, enabled in page_access.items()]
  629. class RolePageComponentPermissionViewSet(viewsets.ViewSet):
  630. """角色权限配置API"""
  631. def get_page_component_permissions(self, request, pk=None):
  632. """获取特定角色的页面访问权限配置"""
  633. try:
  634. role = Role.objects.get(name=pk)
  635. page = request.data.get('page')
  636. if page:
  637. fliterpermissions= role.permissions.filter(page=page)
  638. serializer = self.get_permissions_group(fliterpermissions)
  639. return Response(serializer)
  640. serializer = self.get_permissions_group(role.permissions.all())
  641. return Response(serializer)
  642. except Role.DoesNotExist:
  643. return Response({"error": "Role not found"}, status=404)
  644. def get_permissions_group(self, permissions):
  645. # 获取角色关联的所有权限并预取数据
  646. page_access = {}
  647. for perm in permissions:
  648. if perm.component is not None:
  649. page_access[perm.component] = perm.enabled
  650. # 转换为前端需要的格式
  651. return [{"component": component, "enabled": enabled} for component, enabled in page_access.items()]
  652. class TypeAPIViewSet(viewsets.ModelViewSet):
  653. """
  654. list:
  655. Response a data list(all)
  656. """
  657. pagination_class = MyPageNumberPagination
  658. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  659. ordering_fields = ['id', "create_time", "update_time", ]
  660. filter_class = TypeFilter
  661. def get_queryset(self):
  662. if self.request.user:
  663. return TypeListModel.objects.filter(openid='init_data')
  664. else:
  665. return TypeListModel.objects.none()
  666. def get_serializer_class(self):
  667. if self.action in ['list']:
  668. return serializers.StaffTypeGetSerializer
  669. else:
  670. return self.http_method_not_allowed(request=self.request)
  671. class FileDownloadView(viewsets.ModelViewSet):
  672. renderer_classes = (FileRenderCN,) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
  673. filter_backends = [DjangoFilterBackend, OrderingFilter, ]
  674. ordering_fields = ['id', "create_time", "update_time", ]
  675. filter_class = Filter
  676. def get_project(self):
  677. try:
  678. id = self.kwargs.get('pk')
  679. return id
  680. except:
  681. return None
  682. def get_queryset(self):
  683. id = self.get_project()
  684. if self.request.user:
  685. if id is None:
  686. return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
  687. else:
  688. return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
  689. else:
  690. return ListModel.objects.none()
  691. def get_serializer_class(self):
  692. if self.action in ['list']:
  693. return serializers.FileRenderSerializer
  694. else:
  695. return self.http_method_not_allowed(request=self.request)
  696. def get_lang(self, data):
  697. lang = self.request.META.get('HTTP_LANGUAGE')
  698. if lang:
  699. if lang == 'zh-hans':
  700. return FileRenderCN().render(data)
  701. else:
  702. return FileRenderEN().render(data)
  703. else:
  704. return FileRenderEN().render(data)
  705. def list(self, request, *args, **kwargs):
  706. from datetime import datetime
  707. dt = datetime.now()
  708. data = (
  709. FileRenderSerializer(instance).data
  710. for instance in self.filter_queryset(self.get_queryset())
  711. )
  712. renderer = self.get_lang(data)
  713. response = StreamingHttpResponse(
  714. renderer,
  715. content_type="text/csv"
  716. )
  717. response['Content-Disposition'] = "attachment; filename='staff_{}.csv'".format(
  718. str(dt.strftime('%Y%m%d%H%M%S%f')))
  719. return response