views.py 49 KB

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