from rest_framework.generics import GenericAPIView from rest_framework.mixins import ListModelMixin from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from rest_framework import serializers, status import logging from flower.viewsets import LimitedLimitOffsetPagination from stock import models as stock_models from basic_info import models as basic_models from api_man.serializers import ProductSerializer logger = logging.getLogger(__name__) class WarehouseSimpleSerializer(serializers.ModelSerializer): """仓库简单序列化器""" class Meta: model = basic_models.WareHouse fields = ['id', 'name', 'location', 'area'] class InventorySerializer(serializers.ModelSerializer): """库存项序列化器""" product = ProductSerializer(read_only=True) warehouse = WarehouseSimpleSerializer(read_only=True) class Meta: model = stock_models.Inventory fields = [ 'id', 'product', 'warehouse', 'quantity', 'num_of_rolls', 'spec', ] def __init__(self, *args, **kwargs): """重写初始化方法,确保 context 传递给嵌套序列化器""" super().__init__(*args, **kwargs) # 如果有 context,将其传递给嵌套的序列化器字段 if hasattr(self, 'context'): for field_name, field in self.fields.items(): if isinstance(field, serializers.ModelSerializer): field.context.update(self.context) class InventoryAPIView(ListModelMixin, GenericAPIView): """ 库存查询接口 只返回当前用户所属商户的库存信息 查询参数: - limit: 返回的记录数量,默认20,最大100 - offset: 跳过的记录数量,默认0 - warehouse: 仓库ID,可选 - product: 产品ID,可选 示例: - /api/v1/inventory/ - 获取前20条 - /api/v1/inventory/?limit=50&offset=0 - 获取前50条 - /api/v1/inventory/?limit=20&offset=40 - 跳过前40条,获取接下来的20条 """ serializer_class = InventorySerializer pagination_class = LimitedLimitOffsetPagination permission_classes = [IsAuthenticated] def get_queryset(self): """ 获取查询集,自动过滤当前用户所属商户的库存 """ # 检查用户是否有员工身份 if not hasattr(self.request.user, 'employee'): logger.warning(f"用户 {self.request.user.username} 无员工信息,拒绝访问库存") return stock_models.Inventory.objects.none() employee = self.request.user.employee merchant_id = employee.merchant_id # 使用 select_related 优化查询,只查询当前商户的库存 queryset = stock_models.Inventory.objects.filter( merchant_id=merchant_id ).select_related( 'product', 'product__category', 'warehouse' ).order_by('-quantity', 'product__name') # 可选过滤:按仓库 warehouse_id = self.request.query_params.get('warehouse') if warehouse_id: try: warehouse_id = int(warehouse_id) # 验证仓库属于当前商户 if basic_models.WareHouse.objects.filter( id=warehouse_id, merchant_id=merchant_id ).exists(): queryset = queryset.filter(warehouse_id=warehouse_id) else: logger.warning(f"仓库ID {warehouse_id} 不属于商户 {merchant_id}") return queryset.none() except ValueError: logger.warning(f"warehouse 参数格式错误: {warehouse_id}") return queryset.none() # 可选过滤:按产品 product_id = self.request.query_params.get('product') if product_id: try: product_id = int(product_id) # 验证产品属于当前商户 if basic_models.Product.objects.filter( id=product_id, merchant_id=merchant_id ).exists(): queryset = queryset.filter(product_id=product_id) else: logger.warning(f"产品ID {product_id} 不属于商户 {merchant_id}") return queryset.none() except ValueError: logger.warning(f"product 参数格式错误: {product_id}") return queryset.none() return queryset def get_serializer_context(self): """ 确保序列化器获得 request context """ context = super().get_serializer_context() context['request'] = self.request return context def list(self, request, *args, **kwargs): """ 重写 list 方法,添加统计信息 """ queryset = self.filter_queryset(self.get_queryset()) # 统计信息(在分页前) total_items = queryset.count() total_quantity = sum(item.quantity for item in queryset) total_rolls = sum(item.num_of_rolls for item in queryset) # 使用 ListModelMixin 的分页和序列化逻辑 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) response = self.get_paginated_response(serializer.data) # 添加统计信息 response.data['statistics'] = { 'total_items': total_items, 'total_quantity': float(total_quantity), 'total_rolls': total_rolls, } logger.info( f"用户 {request.user.username} 查询库存列表," f"商户ID: {request.user.employee.merchant_id},共 {total_items} 条记录" ) return response # 如果没有分页,返回全部数据 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) def get(self, request, *args, **kwargs): """ GET 请求处理 """ return self.list(request, *args, **kwargs)