123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- # -*- 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 logging
- 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 Model.models import ProductsScheme
- from Object.RedisObject import RedisObject
- from Object.ResponseObject import ResponseObject
- from Object.TokenObject import TokenObject
- from Ansjer.config import LOGGER
- 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 # 生成二维码
- }
- 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_storage_code(device_type=0):
- """
- 生成入库编码规则:SC+年月日+3位序列号
- 示例:SC20231125-001
- 特性:
- 1. 每日按设备类型独立计数(Redis原子计数器)
- 2. 自动处理分布式并发
- 3. 序列号每日自动重置
- """
- try:
- redis_conn = RedisObject(2)
- # 生成 Redis 键名(格式:SC日期:设备类型)
- date_prefix = datetime.datetime.now().strftime("SC%Y%m%d")
- redis_key = f"{date_prefix}:{device_type}"
- # 原子操作:递增并获取序列号(自动创建并设置过期时间)
- sequence = redis_conn.incr(redis_key)
- # 首次创建时设置过期时间(保留两天防止跨天边界问题)
- if sequence == 1:
- redis_conn.set_expire(redis_key, 172800) # 60*60*24*2 = 2天
- # 检查序列号溢出
- if sequence > 999:
- raise ValueError(f"当日设备类型 {device_type} 的序列号已超过最大值999")
- return f"{date_prefix}-{sequence:03d}"
- except Exception as e:
- LOGGER.error(f"生成storage_code失败: {str(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_storage_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):
- """方案对象序列化(优化点:集中管理序列化逻辑)"""
- 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,
- 'customer_code': scheme.customer_code,
- 'phy':scheme.phy,
- 'remark': scheme.remark,
- '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({
- "storageCode": scheme.storage_code,
- "type": 'NVR' if scheme.device_type == 1 else 'IPC',
- "flash": scheme.flash,
- "DDR": scheme.ddr,
- "mainController": scheme.main_controller,
- "wifi": scheme.wifi,
- "4G": scheme.four_g,
- "AD": scheme.ad,
- "sensor": scheme.sensor,
- 'customerCode': scheme.customer_code,
- 'phy': scheme.phy,
- "orderQuantity": scheme.order_quantity,
- "created_time": datetime.datetime.fromtimestamp(scheme.created_time).strftime("%Y-%m-%d %H:%M:%S")
- })
- 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, "生成失败")
|