# -*- encoding: utf-8 -*- """ @File : ProductsSchemeManageController.py @Time : 2025/5/13 11:52 @Author : stephen @Email : zhangdongming@asj6.wecom.work @Software: PyCharm """ import datetime import json import secrets import string import time from io import BytesIO import qrcode from django.core.paginator import Paginator, EmptyPage from django.db import transaction, IntegrityError from django.db.models import Q from django.http import QueryDict, HttpResponse from django.views import View from Ansjer.config import LOGGER from Model.models import ProductsScheme, DeviceScheme, DeviceTypeModel, CompanySerialModel from Object.ResponseObject import ResponseObject from Object.TokenObject import TokenObject class ProductsSchemeManageView(View): def get(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.GET, request, operation) def post(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') return self.validation(request.POST, request, operation) def delete(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') delete = QueryDict(request.body) if not delete: delete = request.GET return self.validation(delete, request, operation) def put(self, request, *args, **kwargs): request.encoding = 'utf-8' operation = kwargs.get('operation') put = QueryDict(request.body) return self.validation(put, request, operation) def validation(self, request_dict, request, operation): """请求验证路由""" # 初始化响应对象 language = request_dict.get('language', 'cn') response = ResponseObject(language, 'pc') # Token验证 try: tko = TokenObject( request.META.get('HTTP_AUTHORIZATION'), returntpye='pc') if tko.code != 0: return response.json(tko.code) response.lang = tko.lang user_id = tko.userID except Exception as e: LOGGER.error(f"Token验证失败: {str(e)}") return response.json(444) # 操作路由映射 operation_handlers = { 'queryList': self.query_list, # 查询列表 'add': self.add_scheme, # 添加方案 'edit': self.edit_scheme, # 编辑方案 'delete': self.delete_scheme, # 删除方案 'generateQR': self.generate_qr_code, # 生成二维码 'deviceSchemeList': self.device_scheme_list } handler = operation_handlers.get(operation) if not handler: return response.json(444, 'operation') try: return handler(user_id, request_dict, response) except Exception as e: LOGGER.error(f"操作{operation}执行异常:{repr(e)}") return response.json(500, "服务器内部错误") def query_list(self, user_id, request_dict, response): """查询方案列表(优化点:分页封装/字段映射)""" # 参数处理与验证 try: page = int(request_dict.get('page', 1)) page_size = min(int(request_dict.get('pageSize', 10)), 100) # 限制最大分页大小 except ValueError: return response.json(444, "分页参数错误") # 构建查询条件 query = Q(deleted=False) # 特殊字段处理示例(如果需要) if order_number := request_dict.get('orderNumber'): query &= Q(order_number__icontains=order_number) if storage_code := request_dict.get('storageCode'): query &= Q(storage_code__icontains=storage_code) if device_type := request_dict.get('deviceType'): query &= Q(device_type=int(device_type)) if flash := request_dict.get('flash'): query &= Q(flash__icontains=flash) if ddr := request_dict.get('ddr'): query &= Q(ddr__icontains=ddr) if main_controller := request_dict.get('mainController'): query &= Q(main_controller__icontains=main_controller) if wifi := request_dict.get('wifi'): query &= Q(wifi__icontains=wifi) if four_g := request_dict.get('fourG'): query &= Q(four_g__icontains=four_g) if ad := request_dict.get('ad'): query &= Q(ad__icontains=ad) if sensor := request_dict.get('sensor'): query &= Q(sensor__icontains=sensor) # 使用分页器 queryset = ProductsScheme.objects.filter(query).order_by('-created_time') paginator = Paginator(queryset, page_size) try: page_obj = paginator.page(page) except EmptyPage: return response.json(444, "页码超出范围") # 序列化数据 data = [self._scheme_to_dict(scheme) for scheme in page_obj] return response.json(0, { 'list': data, 'total': paginator.count, 'currentPage': page_obj.number, 'totalPages': paginator.num_pages }) @staticmethod def generate_timestamp_code(device_type: int = 0) -> str: """ 方案1:基于时间戳的编码 格式:PC+设备类型+年月日+时分秒+6位随机大写字符 示例:PC020250519143022-ABCDEF """ try: current_time = datetime.datetime.now() date_part = current_time.strftime("%Y%m%d%H%M%S") # 使用string.ascii_uppercase生成大写随机字符 random_part = ''.join(secrets.choice(string.ascii_uppercase) for _ in range(3)) return f"{date_part}-{device_type}-{random_part}" except Exception as e: LOGGER.error(f"生成编码失败: {repr(e)}") raise def add_scheme(self, user_id, request_dict, response): """新增方案(集成自动编码生成)""" try: device_type = int(request_dict.get('deviceType', 0)) # 生成唯一storage_code storage_code = self.generate_timestamp_code(device_type) # 构造方案数据 scheme_data = { 'storage_code': storage_code, # 使用生成的编码 'order_number': request_dict.get('orderNumber', ''), 'device_type': device_type, 'flash': request_dict.get('flash', ''), 'ddr': request_dict.get('ddr', ''), 'main_controller': request_dict.get('mainController', ''), 'wifi': request_dict.get('wifi', ''), 'four_g': request_dict.get('fourG', ''), 'ad': request_dict.get('ad', ''), 'sensor': request_dict.get('sensor', ''), 'order_quantity': int(request_dict.get('orderQuantity', 0)), 'customer_code': request_dict.get('customerCode', ''), 'phy': request_dict.get('phy', ''), 'remark': request_dict.get('remark', ''), 'created_time': int(time.time()), 'updated_time': int(time.time()), 'created_by': user_id, 'updated_by': user_id } scheme = ProductsScheme.objects.create(**scheme_data) return response.json(0, { 'id': scheme.id, 'storageCode': storage_code # 返回生成的编码 }) except IntegrityError as e: LOGGER.error(f"唯一性冲突: {str(e)}") return response.json(173, "数据已存在") except ValueError as e: return response.json(444, "参数类型错误") except Exception as e: LOGGER.exception(f"添加方案异常:{repr(e)}") return response.json(500, "生成编码失败") # 可编辑字段白名单(前端字段名: 模型字段名) EDITABLE_FIELDS = { 'orderNumber': 'order_number', 'deviceType': 'device_type', 'flash': 'flash', 'ddr': 'ddr', 'mainController': 'main_controller', 'wifi': 'wifi', 'fourG': 'four_g', 'ad': 'ad', 'sensor': 'sensor', 'orderQuantity': 'order_quantity', 'customerCode': 'customer_code', 'phy': 'phy', 'remark': 'remark' } # 需要类型转换的字段配置(字段名: 转换函数) FIELD_CONVERTERS = { 'deviceType': int, 'orderQuantity': int } @transaction.atomic def edit_scheme(self, user_id, request_dict, response): """ 方案编辑接口(事务原子性保证) 优化特性: 1. 前端字段到模型字段的自动映射 2. 字段级白名单过滤 3. 智能类型转换 4. 最小化更新字段 5. 并发安全控制 """ # ================= 参数校验阶段 ================= if 'id' not in request_dict: return response.json(444, "缺少方案ID") try: scheme_id = int(request_dict['id']) except (ValueError, TypeError): LOGGER.warning(f"非法方案ID: {request_dict.get('id')}") return response.json(444, "方案ID格式错误") # ================= 数据获取阶段 ================= try: # 使用select_for_update锁定记录,防止并发修改 scheme = ProductsScheme.objects.select_for_update().get( id=scheme_id, deleted=False ) except ProductsScheme.DoesNotExist: LOGGER.info(f"方案不存在: {scheme_id}") return response.json(173, "方案不存在") except Exception as e: LOGGER.error(f"数据库查询异常: {str(e)}") return response.json(500, "系统错误") # ================= 数据处理阶段 ================= update_fields = [] update_data = {'updated_time': int(time.time()), 'updated_by': user_id} # 遍历所有请求参数 for frontend_field, value in request_dict.items(): # 1. 白名单校验 if frontend_field not in self.EDITABLE_FIELDS: continue # 2. 获取对应的模型字段名 model_field = self.EDITABLE_FIELDS[frontend_field] # 3. 类型转换处理 if frontend_field in self.FIELD_CONVERTERS: try: value = self.FIELD_CONVERTERS[frontend_field](value) except (ValueError, TypeError): LOGGER.warning(f"字段类型错误 {frontend_field}={value}") return response.json(444, f"{frontend_field}类型不合法") # 4. 值变更检查(避免无意义更新) if getattr(scheme, model_field) != value: update_data[model_field] = value update_fields.append(model_field) # 无实际修改时快速返回 if not update_fields: LOGGER.debug("无字段变更") return response.json(0) # ================= 数据持久化阶段 ================= try: # 动态生成更新SQL:UPDATE ... SET field1=%s, field2=%s ProductsScheme.objects.filter(id=scheme_id).update(**update_data) LOGGER.info(f"方案更新成功: {scheme_id} 修改字段: {update_fields}") return response.json(0) except IntegrityError as e: LOGGER.error(f"数据唯一性冲突: {str(e)}") return response.json(177) except Exception as e: LOGGER.exception("方案更新异常") return response.json(500, "系统错误") def delete_scheme(self, user_id, request_dict, response): """删除方案(优化点:逻辑删除优化)""" # 参数校验 if 'id' not in request_dict: return response.json(444, "缺少方案ID") try: # 使用update直接进行逻辑删除,提高效率 rows = ProductsScheme.objects.filter( id=request_dict['id'], deleted=False ).update( deleted=True, updated_by=user_id, updated_time=int(time.time()) ) if rows == 0: return response.json(173, "方案不存在") return response.json(0) except ValueError: return response.json(500, "方案ID格式错误") def _scheme_to_dict(self, scheme): """方案对象序列化(优化点:集中管理序列化逻辑)""" # 定义需要包含在str_schema中的字段 schema_fields = [ 'flash', 'main_controller', 'wifi', 'four_g', 'sensor', 'ddr', 'ad', 'phy' ] # 获取字段值,过滤掉空值 schema_values = [getattr(scheme, field, '') for field in schema_fields] valid_values = [v for v in schema_values if v] # 过滤空字符串 # 拼接字符串模式,不保留空值位置 str_schema = '+'.join(valid_values) return { 'id': scheme.id, 'orderNumber': scheme.order_number, 'storageCode': scheme.storage_code, 'deviceType': scheme.device_type, 'flash': scheme.flash, 'ddr': scheme.ddr, 'mainController': scheme.main_controller, 'wifi': scheme.wifi, 'fourG': scheme.four_g, 'ad': scheme.ad, 'sensor': scheme.sensor, 'orderQuantity': scheme.order_quantity, 'customerCode': scheme.customer_code, 'phy': scheme.phy, 'remark': scheme.remark, 'strSchema': str_schema, 'createdTime': scheme.created_time, 'createdBy': scheme.created_by } def generate_qr_code(self, user_id, request_dict, response): """生成方案二维码""" try: scheme_id = int(request_dict.get('scheme_id')) scheme = ProductsScheme.objects.get(id=scheme_id, deleted=False) # 创建二维码(示例生成包含方案ID+名称) qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) # 组织二维码内容(根据业务需求自定义) qr_data = json.dumps({ "sc": scheme.storage_code, "t": 'NVR' if scheme.device_type == 1 else 'IPC', "f": scheme.flash, "ddr": scheme.ddr, "mc": scheme.main_controller, "wifi": scheme.wifi, "mode4G": scheme.four_g, "ad": scheme.ad, "s": scheme.sensor, 'uc': scheme.customer_code, 'phy': scheme.phy, "num": scheme.order_quantity, "ct": scheme.created_time }) qr.add_data(qr_data) qr.make(fit=True) # 生成图片 img = qr.make_image(fill_color="black", back_color="white") # 将图片转为字节流 buffer = BytesIO() img.save(buffer, format="PNG") # 返回文件响应 return HttpResponse( buffer.getvalue(), content_type="image/png", headers={ 'Content-Disposition': f'attachment; filename="scheme_{scheme.id}_qr.png"' } ) except ProductsScheme.DoesNotExist: return response.json(173, "方案不存在") except Exception as e: LOGGER.error(f"生成二维码失败: {str(e)}") return response.json(500, "生成失败") @staticmethod def device_scheme_list(user_id, request_dict, response): """ 查询设备方案列表 @param request_dict: 请求参数 @param response: 响应对象 @return: 响应对象包含设备方案列表 """ # 提取请求参数并提供默认值 filters = { 'serial_number': request_dict.get("serialNumber"), 'storage_code': request_dict.get("storageCode"), 'device_type': request_dict.get("deviceType"), 'phone_model': request_dict.get("phoneModel"), } page = int(request_dict.get("page", 1)) page_size = int(request_dict.get("pageSize", 20)) try: # 构建基础查询集 queryset = DeviceScheme.objects.all().order_by('-created_time') # 应用过滤条件 if filters['serial_number']: queryset = queryset.filter(serial_number=filters['serial_number']) if filters['storage_code']: queryset = queryset.filter(storage_code__icontains=filters['storage_code']) if filters['device_type']: queryset = queryset.filter(device_type=filters['device_type']) if filters['phone_model']: queryset = queryset.filter(phone_model__icontains=filters['phone_model']) # 分页处理 paginator = Paginator(queryset, page_size) try: current_page = paginator.page(page) except EmptyPage: current_page = paginator.page(paginator.num_pages) # 批量预取关联数据 device_list = [] if current_page.object_list: # 准备批量查询所需数据 storage_codes = {device.storage_code for device in current_page} serial_prefixes = {device.serial_number[:6] for device in current_page} device_types = {device.device_type for device in current_page} # 批量获取关联数据 product_schemes_map = { ps.storage_code: ps for ps in ProductsScheme.objects.filter(storage_code__in=storage_codes) } device_type_names = { dt['type']: dt['name'] for dt in DeviceTypeModel.objects.filter(type__in=device_types).values('type', 'name') } serial_statuses = { cs.serial_number: cs for cs in CompanySerialModel.objects.filter(serial_number__in=serial_prefixes) } # 构建设备数据 device_list = [ ProductsSchemeManageView.build_device_data( device, product_schemes_map.get(device.storage_code), device_type_names.get(device.device_type, device.device_type), serial_statuses.get(device.serial_number[:6]) ) for device in current_page ] return response.json(0, { 'list': device_list, 'total': paginator.count }) except Exception as e: LOGGER.error("设备方案列表查询异常") return response.json(500, f'服务器错误: {str(e)}') @staticmethod def build_device_data(device, product_scheme, device_type_name, serial_status): """构建单个设备数据字典""" # 设备基础数据 device_data = { 'id': device.id, 'serialNumber': device.serial_number, 'deviceType': device_type_name, 'phoneModel': device.phone_model, 'storageCode': device.storage_code, 'createdTime': device.created_time, 'updatedTime': device.updated_time, 'snStatus': 'N/A', 'aTime': 'N/A', } # 序列号激活信息 if device.device_type != 0 and serial_status: device_data['snStatus'] = '已激活' if serial_status.status > 1 else '未激活' device_data['aTime'] = datetime.datetime.fromtimestamp( serial_status.update_time ).strftime('%Y-%m-%d %H:%M:%S') # 添加产品方案信息 if product_scheme: # 构建方案数据字符串 scheme_parts = [ product_scheme.flash, product_scheme.ddr, product_scheme.main_controller, product_scheme.wifi, product_scheme.four_g, product_scheme.ad, product_scheme.phy, product_scheme.sensor ] scheme_data = '+'.join(filter(None, map(str, scheme_parts))) device_data.update({ 'orderNumber': product_scheme.order_number or '', 'type': 'NVR' if product_scheme.device_type == 1 else 'IPC', 'flash': product_scheme.flash or '', 'ddr': product_scheme.ddr or '', 'mainController': product_scheme.main_controller or '', 'wifi': product_scheme.wifi or '', 'fourG': product_scheme.four_g or '', 'ad': product_scheme.ad or '', 'phy': product_scheme.phy or '', 'sensor': product_scheme.sensor or '', 'orderQuantity': product_scheme.order_quantity or '', 'customerCode': product_scheme.customer_code or '', 'schemeData': scheme_data, 'remark': product_scheme.remark or '' }) return device_data