forked from erp-dev/erp
feat: sse && multi_merchant completed
This commit is contained in:
185
api_v1/views/product_image.py
Normal file
185
api_v1/views/product_image.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
产品图片上传视图
|
||||
|
||||
处理前后端分离场景下的图片上传
|
||||
"""
|
||||
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework import status
|
||||
from basic_info.models import Product
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProductImageUploadView(APIView):
|
||||
"""
|
||||
产品图片上传接口
|
||||
|
||||
支持两种方式:
|
||||
1. 直接上传图片文件(multipart/form-data)
|
||||
2. 更新已有产品的图片
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
def post(self, request, product_id):
|
||||
"""
|
||||
上传/更新产品图片
|
||||
|
||||
请求参数:
|
||||
- product_id: 产品 ID(URL 路径参数)
|
||||
- image: 图片文件(multipart/form-data)
|
||||
|
||||
前端使用示例:
|
||||
```javascript
|
||||
const formData = new FormData();
|
||||
formData.append('image', file);
|
||||
|
||||
fetch('/api/v1/products/{id}/image/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
```
|
||||
"""
|
||||
# 检查用户权限
|
||||
if not hasattr(request.user, 'employee'):
|
||||
return Response(
|
||||
{'error': '用户无员工信息,无权操作'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
|
||||
merchant_id = request.user.employee.merchant_id
|
||||
|
||||
# 获取产品
|
||||
try:
|
||||
product = Product.objects.get(id=product_id, merchant_id=merchant_id)
|
||||
except Product.DoesNotExist:
|
||||
return Response(
|
||||
{'error': f'产品 ID {product_id} 不存在或不属于当前商户'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
# 获取上传的图片
|
||||
image_file = request.FILES.get('image')
|
||||
if not image_file:
|
||||
return Response(
|
||||
{'error': '请提供图片文件(字段名:image)'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# 验证文件类型
|
||||
allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
|
||||
if image_file.content_type not in allowed_types:
|
||||
return Response(
|
||||
{
|
||||
'error': f'不支持的图片格式:{image_file.content_type}',
|
||||
'allowed_types': allowed_types
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# 验证文件大小(最大 5MB)
|
||||
max_size = 5 * 1024 * 1024 # 5MB
|
||||
if image_file.size > max_size:
|
||||
return Response(
|
||||
{
|
||||
'error': f'图片大小超过限制:{image_file.size} bytes',
|
||||
'max_size': f'{max_size / 1024 / 1024}MB'
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
# 保存图片(会自动上传到七牛云)
|
||||
old_image = product.image
|
||||
product.image = image_file
|
||||
product.save(update_fields=['image', 'updated_at'])
|
||||
|
||||
# 删除旧图片(如果存在)
|
||||
# 注意:七牛云的 delete 需要特殊处理,这里只是示例
|
||||
if old_image:
|
||||
try:
|
||||
old_image.delete(save=False)
|
||||
except Exception as e:
|
||||
logger.warning(f"删除旧图片失败: {e}")
|
||||
|
||||
# 构建图片 URL
|
||||
image_url = request.build_absolute_uri(product.image.url)
|
||||
|
||||
logger.info(
|
||||
f"产品 {product_id} 图片上传成功,"
|
||||
f"用户:{request.user.username},大小:{image_file.size} bytes"
|
||||
)
|
||||
|
||||
return Response({
|
||||
'message': '图片上传成功',
|
||||
'product_id': product.id,
|
||||
'image_url': image_url,
|
||||
'image_name': product.image.name,
|
||||
'size': image_file.size,
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"图片上传失败: {str(e)}", exc_info=True)
|
||||
return Response(
|
||||
{'error': '图片上传失败', 'message': str(e)},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
def delete(self, request, product_id):
|
||||
"""
|
||||
删除产品图片
|
||||
|
||||
请求参数:
|
||||
- product_id: 产品 ID(URL 路径参数)
|
||||
"""
|
||||
# 检查用户权限
|
||||
if not hasattr(request.user, 'employee'):
|
||||
return Response(
|
||||
{'error': '用户无员工信息,无权操作'},
|
||||
status=status.HTTP_403_FORBIDDEN
|
||||
)
|
||||
|
||||
merchant_id = request.user.employee.merchant_id
|
||||
|
||||
# 获取产品
|
||||
try:
|
||||
product = Product.objects.get(id=product_id, merchant_id=merchant_id)
|
||||
except Product.DoesNotExist:
|
||||
return Response(
|
||||
{'error': f'产品 ID {product_id} 不存在或不属于当前商户'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
if not product.image:
|
||||
return Response(
|
||||
{'message': '产品没有图片'},
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
try:
|
||||
# 删除图片
|
||||
product.image.delete(save=False)
|
||||
product.image = None
|
||||
product.save(update_fields=['image', 'updated_at'])
|
||||
|
||||
logger.info(f"产品 {product_id} 图片已删除,用户:{request.user.username}")
|
||||
|
||||
return Response({
|
||||
'message': '图片删除成功',
|
||||
'product_id': product.id
|
||||
}, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"图片删除失败: {str(e)}", exc_info=True)
|
||||
return Response(
|
||||
{'error': '图片删除失败', 'message': str(e)},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
Reference in New Issue
Block a user