forked from erp-dev/erp
feat: sse && multi_merchant completed
This commit is contained in:
176
api_v1/views/inventory.py
Normal file
176
api_v1/views/inventory.py
Normal file
@@ -0,0 +1,176 @@
|
||||
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
|
||||
from rest_framework.pagination import LimitOffsetPagination
|
||||
from stock import models as stock_models
|
||||
from basic_info import models as basic_models
|
||||
from api_man.serializers import ProductSerializer
|
||||
import logging
|
||||
|
||||
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 = LimitOffsetPagination
|
||||
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)
|
||||
Reference in New Issue
Block a user