import logging import random import string import traceback import uuid from django.db import transaction from django.db.models import Q from django_filters.rest_framework import DjangoFilterBackend from rest_framework.decorators import action from rest_framework.filters import SearchFilter, OrderingFilter from rest_framework.permissions import IsAuthenticated from rest_framework_jwt.authentication import JSONWebTokenAuthentication from ChaCeRndTrans import settings from ChaCeRndTrans.basic import CCAIResponse from ChaCeRndTrans.code import SERVER_ERROR, BAD, OK from ChaCeRndTrans.settings import RELEASE_DOMAIN, TEST_DOMAIN from rbac.models import UserProfile, Role, Company from rbac.serializers.role_serializer import RoleListSerializer from rbac.serializers.user_serializer import UserListSerializer, CompanyRoleSerializer, UserCreateSerializer, \ UserModifySerializer from utils.custom import CustomViewBase, sha1_encrypt, CommonPagination, is_valid_username logger = logging.getLogger('error') class SubAccountViewSet(CustomViewBase): ''' 子账号 ''' perms_map = ( {"*": "admin"}, {"*": "sub_account_all"}, {"get": "sub_account_list"}, {"post": "sub_account_create"}, {"put": "sub_account_edit"}, {"delete": "sub_account_delete"} ) queryset = UserProfile.objects.all() serializer_class = UserListSerializer pagination_class = CommonPagination filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter) filter_fields = ("is_active",) search_fields = ("username", "name", "mobile", "email") ordering_fields = ("id",) authentication_classes = (JSONWebTokenAuthentication,) def get_serializer_class(self): # 根据请求类型动态变更serializer if self.action == "create": return UserCreateSerializer elif self.action == "list": return UserListSerializer return UserModifySerializer def get_queryset(self): """ 我的企业-子账号 1.后台人员直接查看当前公司所有子用户 2.前台用户只能查看当前公司,当前用户的子用户 """ companyMid = self.request.query_params.get('companyMid', None) parent_id = self.request.query_params.get('parent_id', None) queryset = UserProfile.objects.all() query = Q() if 1 == self.request.user.label: # 前台用户 if parent_id: # 查询当前用户的子账号 query &= Q(parent_id=parent_id) if companyMid: # 且子账号需要有关联本公司 query &= Q(company__MainId=companyMid) else: # 后台用户 if companyMid: # 且子账号需要有关联本公司 query &= Q(company__MainId=companyMid) query &= ~Q(parent_id=None) queryset = queryset.filter(query) return queryset # def list(self, request, *args, **kwargs): # pagination = {} # try: # params = request.GET # page_size = params.get('size', None) # page = params.get('page', None) # name = params.get("name", None) # 子账号真实姓名 # mobile = params.get("mobile", None) # 手机号码 # is_active = params.get("is_active", None) # 是否激活 # if page is None: # page = 1 # if page_size is None: # page_size = 10 # start_index = (int(page) - 1) * int(page_size) # # parent_id = params.get("parent_id", None) # if parent_id is None: # return CCAIResponse("缺少参数parent_id", BAD) # main_account = UserProfile.objects.filter(id=parent_id).first() # count = 0 # row = [] # sql = """ # select U.id, U.username, U.is_active, U.mobile, U.name # from rbac_userprofile as U # where U.is_sub = 1 and U.parent_id = {} # """.format(parent_id) # sql_count = """ # select U.id, count(U.id) as count # from rbac_userprofile as U # where U.is_sub = 1 and U.parent_id = {} # """.format(parent_id) # if name: # temp = r""" and U.name = '{}' """.format(name) # sql = sql + temp # sql_count = sql_count + temp # if mobile: # temp = r""" and U.mobile = '{}' """.format(mobile) # sql = sql + temp # sql_count = sql_count + temp # if is_active: # temp = r""" and U.is_active = '{}' """.format(is_active) # sql = sql + temp # sql_count = sql_count + temp # sql = sql + """ ORDER BY U.id desc limit {}, {} """.format(start_index, int(page_size)) # sub_account = UserProfile.objects.raw(sql) # count_result = UserProfile.objects.raw(sql_count) # if len(count_result) > 0: # count = count_result[0].count # if sub_account: # for item in sub_account: # item.__dict__.pop('_state') # row.append(item.__dict__) # return CCAIResponse(data=row, count=count) # except Exception as e: # logger.error("get sub account list failed: \n%s" % traceback.format_exc()) # return CCAIResponse("获取子账号列表失败", SERVER_ERROR) def list(self, request, *args, **kwargs): try: queryset = self.filter_queryset(self.get_queryset().filter().all()) page = self.paginate_queryset(queryset) domain = RELEASE_DOMAIN if settings.DEVELOP_DEBUG: domain = TEST_DOMAIN if page is not None: serializer = self.get_serializer(page, many=True) return_data = serializer.data # for item in return_data: # start = item['image'].rindex('/media/') # item['image'] = domain + item['image'][start:] return self.get_paginated_response(return_data) serializer = self.get_serializer(queryset, many=True) return_data = serializer.data for item in return_data: start = item['image'].rindex('/media/') item['image'] = domain + item['image'][start:] return CCAIResponse(data=return_data, status=200) except Exception as e: logger.error("get sub account list failed: \n%s" % traceback.format_exc()) return CCAIResponse(msg='获取子用户列表失败', status=SERVER_ERROR) def create(self, request, *args, **kwargs): try: companyMid = request.query_params.get('companyMid', None) data = request.data.copy() company = data.get('company') roles = data.get('roles') if request.user.is_sub == 1: return CCAIResponse("本账号为子账号,不允许开设子账号!", BAD) # 验证公司信息是否有效 # if companyMid: # company = Company.objects.filter(MainId=companyMid).first() # if roles: # roleid_list = roles.split(',') # # if companies: # companies_list = companies.split(',') # if not data['parent_id']: # return CCAIResponse(data="缺少参数parent_id", status=BAD) user_count = UserProfile.objects.filter(username=data['username']).count() if user_count > 0: return CCAIResponse(data="用户名冲突,请重新输入", msg="用户名冲突,请重新输入", status=BAD) if data['mobile']: user_count = UserProfile.objects.filter(mobile=data['mobile']).count() if user_count > 0: return CCAIResponse(data="手机号码已被注册,请校对手机号码", msg="手机号码已被注册,请校对手机号码", status=BAD) if data['username']: if not is_valid_username(data['username']): return CCAIResponse(data="用户名命名不符合规定,只能使用汉字、英文字母、数字,不能使用特殊字符。", msg="用户名命名不符合规定,只能使用汉字、英文字母、数字,不能使用特殊字符。", status=BAD) with transaction.atomic(): # 生成用户与公司的全局id userMid = uuid.uuid4().__str__() # 创建用户 user = UserProfile() user.MainId = userMid user.companyMid = companyMid # 默认绑定当前公司 user.username = data['username'] user.name = data['username'] user.mobile = data['mobile'] user.is_sub = 1 # 子账号账号 user.is_active = 1 # 注册即激活 user.label = 1 # 前台用户 user.parent_id = request.user.id # 创建随机密码,并返回 random_password = ''.join(random.sample(string.ascii_letters + string.digits, 8)) password = sha1_encrypt(random_password) user.set_password(password) user.save() com_list = Company.objects.filter(id__in=company) for c in com_list: # 为注册用户添加公司 user.company.add(c) # 添加默认角色测试服29 / 正式服 32 if settings.DEBUG: role = Role.objects.get(id=29) else: role = Role.objects.get(id=32) user.roles.add(role) if roles and len(roles) > 0: # 为子账号添加角色 roles = Role.objects.filter(id__in=roles).all() for role in roles: user.roles.add(role) return CCAIResponse(data=random_password, status=OK) except Exception: logger.error("user:%s, add sub account failed: \n%s" % (request.user.id, traceback.format_exc())) return CCAIResponse("创建子账号失败", SERVER_ERROR) def update(self, request, *args, **kwargs): try: data = request.data.copy() partial = kwargs.pop('partial', False) if 'parent_id' not in data and data['parent_id'] is None: return CCAIResponse("缺少参数parent_id", BAD) # roles = data.get('roles') # companies = data.get('companies') # if roles: # roles = roles.split(',') # data['roles'] =roles # if companies: # companies = companies.split(',') # data['companies'] =companies instance = self.get_object() serializer = self.get_serializer(instance, data=data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) user = UserProfile.objects.filter(id=data['id']).first() if user is None: return CCAIResponse("账号不存在", SERVER_ERROR) if data['mobile']: user_count = UserProfile.objects.filter(mobile=data['mobile']).exclude(id=data['id']).count() if user_count > 0: return CCAIResponse(data="手机号码已被注册,请校对手机号码", msg="手机号码已被注册,请校对手机号码", status=BAD) if data['username']: if not is_valid_username(data['username']): return CCAIResponse(data="用户名格式不对,只能输入汉字,英文字母,数字!", msg="用户名格式不对,只能输入汉字,英文字母,数字!", status=BAD) user_count = UserProfile.objects.filter(username=data['username']).exclude(id=data['id']).count() if user_count > 0: return CCAIResponse("该用户名已被注册,请校对用户名", status=BAD) # if data['ratio']: # ratio = Decimal(data['ratio']) # 子账号的分销比例 = 父账号的分销比例 * 父账号为其分配的分销比例 # user.ratio = ratio user.username = data['username'] user.mobile = data['mobile'] user.save() return CCAIResponse("success") except Exception as e: logger.error("user:%s, update sub account failed: \n%s" % (request.user.id, traceback.format_exc())) return CCAIResponse("更新子账号失败", SERVER_ERROR) def destroy(self, request, *args, **kwargs): try: with transaction.atomic(): instance = self.get_object() self.perform_destroy(instance) users = UserProfile.objects.filter(username=instance.username) for user in users: user.delete() return CCAIResponse(data="删除账号成功") except Exception as e: logger.error("user:%s, delete sub account failed: \n%s" % (request.user.id, traceback.format_exc())) return CCAIResponse(data="删除子账号失败", msg="删除子账号失败", status=SERVER_ERROR) @action(methods=["POST"], detail=False, permission_classes=[IsAuthenticated], url_path="disable_sub_user", url_name="disable_sub_user") def disable_sub_user(self, request): try: data = request.data.copy() if 'uid' not in data and data['uid'] is None: return CCAIResponse(data="缺少参数uid", msg="缺少参数uid", status=BAD) if 'is_active' not in data: return CCAIResponse(data="缺少参数is_active", msg="缺少参数is_active", status=BAD) user = UserProfile.objects.filter(id=data['uid']).first() if user is None: return CCAIResponse(data="账号不存在", msg="账号不存在", status=SERVER_ERROR) if data['is_active']: user.is_active = 1 else: user.is_active = 0 user.save() return CCAIResponse(data="修改子账号激活状态") except Exception as e: logger.error( "user: %s disable sub user failed: \n%s" % (request.user.id, traceback.format_exc())) return CCAIResponse(msg='修改子账号激活状态失败', status=SERVER_ERROR) @action(methods=["post"], detail=False, permission_classes=[IsAuthenticated], url_path="reset_sub_password", url_name="reset_sub_password") def reset_subAccount_password(self, request, pk=None): try: data = request.data.copy() if 'uid' not in data and data['uid'] is None: return CCAIResponse(data="缺少参数uid", msg="缺少参数uid", status=BAD) user = UserProfile.objects.get(id=data['uid']) if user and request.user.is_sub == 2: with transaction.atomic(): random_password = ''.join(random.sample(string.ascii_lowercase + string.digits, 8)) password = sha1_encrypt(random_password) user.set_password(password) user.save() return CCAIResponse(data=random_password, status=OK) else: return CCAIResponse(data="子账号异常,请联系管理员", status=BAD) except Exception as e: logger.error("main account reset sub account password failed: \n%s" % traceback.format_exc()) return CCAIResponse(msg='修改子账号密码失败', status=SERVER_ERROR) # @action(methods=["post"], detail=False, permission_classes=[IsAuthenticated], # url_path="set_sub_ratio", url_name="set_sub_ratio") # def set_subAcount_ratio(self, request, pk=None): # try: # data = request.data.copy() # if 'uid' not in data and data['uid'] is None: # return CCAIResponse(data="缺少参数uid", msg="缺少参数uid", status=BAD) # if 'ratio' not in data and data['ratio'] is None: # return CCAIResponse(data="缺少参数ratio", msg="缺少参数ratio", status=BAD) # user = UserProfile.objects.get(id=data['uid']) # if user and request.user.is_sub == 2: # user.ratio = data['ratio'] # user.save() # return CCAIResponse(data="修改子账号佣金分销比例成功!", msg="修改子账号佣金分销比例成功!", status=OK) # except Exception as e: # logger.error("main account set sub account ratio failed: \n%s" % traceback.format_exc()) # return CCAIResponse(msg='修改子账号佣金分销比例失败', status=SERVER_ERROR) @action(methods=["get"], detail=False, permission_classes=[IsAuthenticated], url_path="getCompanyAndRole", url_name="getCompanyAndRole") def getCompanyAndRole(self, request): """ 获取当前用户的关联公司与该公司的角色,及系统默认提供的角色(除去后台角色label=2) """ try: # 获取公司 companyList = request.user.company.all() companyMainIdList = [item.MainId for item in companyList] # 获取角色 rolelist = Role.objects.filter( ~Q(label=2) & (Q(companyMid__in=companyMainIdList) | Q(companyMid__isnull=True))) sysRole = set() # 系统默认角色 # 将角色封装到对应的公司中 (当前方法时间复杂度较高,有提升空间) for com in companyList: com.roles = [] for role in rolelist: if role.companyMid and role.companyMid != '': if role.companyMid == com.MainId: com.roles.append(role) else: sysRole.add(role) data = { 'sysRole': RoleListSerializer(sysRole, many=True).data, 'comRole': CompanyRoleSerializer(companyList, many=True).data, } return CCAIResponse(data) except Exception as e: logger.error("getCompanyAndRole failed: \n%s" % traceback.format_exc()) return CCAIResponse(msg='获取公司与角色映射失败', status=SERVER_ERROR)