from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from django.core.files.storage import default_storage from django.conf import settings from PIL import Image import os from .models import Project, Asset from .serializers import ProjectSerializer, ProjectDetailSerializer, AssetSerializer @api_view(['GET', 'POST']) def project_list(request): """获取项目列表或创建新项目""" if request.method == 'GET': projects = Project.objects.all() serializer = ProjectSerializer(projects, many=True) return Response(serializer.data) elif request.method == 'POST': # 自动补默认 design/card_overrides/number_layout data = dict(request.data or {}) if 'design' not in data: data['design'] = Project._meta.get_field('design').default() if 'card_overrides' not in data: data['card_overrides'] = Project._meta.get_field('card_overrides').default() if 'number_layout' not in data: data['number_layout'] = Project._meta.get_field('number_layout').default() if 'back_design' not in data: data['back_design'] = Project._meta.get_field('back_design').default() # 抽出 template_id(不写进 Project 字段) template_id = data.pop('template_id', None) serializer = ProjectSerializer(data=data) if serializer.is_valid(): project = serializer.save() # 如果传了 template_id,自动套用模板预设 template_apply_result = None if template_id: from apps.templates.models import CardTemplate from apps.templates.template_apply import apply_template_to_project try: tpl = CardTemplate.objects.get(pk=template_id) template_apply_result = apply_template_to_project(project, tpl) except CardTemplate.DoesNotExist: template_apply_result = {'error': f'template {template_id} not found'} # 重新读一次(apply_template 已修改了 design 和 assets) project.refresh_from_db() return Response({ **ProjectSerializer(project).data, 'template_apply': template_apply_result, }, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['GET', 'PUT', 'DELETE']) def project_detail(request, pk): """获取、更新或删除项目""" try: project = Project.objects.get(pk=pk) except Project.DoesNotExist: return Response({'error': 'Project not found'}, status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': serializer = ProjectDetailSerializer(project) return Response(serializer.data) elif request.method == 'PUT': serializer = ProjectSerializer(project, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': project.delete() return Response(status=status.HTTP_204_NO_CONTENT) @api_view(['POST']) def project_save_design(request, pk): """整体保存项目设计(design / card_overrides / number_layout)""" try: project = Project.objects.get(pk=pk) except Project.DoesNotExist: return Response({'error': 'Project not found'}, status=status.HTTP_404_NOT_FOUND) for field in ('design', 'back_design', 'card_overrides', 'number_layout', 'face_orientations'): if field in request.data: setattr(project, field, request.data[field]) project.save() return Response({ 'ok': True, 'design': project.design, 'back_design': project.back_design, 'card_overrides': project.card_overrides, 'number_layout': project.number_layout, 'face_orientations': project.face_orientations, }) @api_view(['GET', 'POST']) def asset_list(request, project_pk): """获取项目素材列表或上传新素材""" try: project = Project.objects.get(pk=project_pk) except Project.DoesNotExist: return Response({'error': 'Project not found'}, status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': assets = project.assets.all() serializer = AssetSerializer(assets, many=True, context={'request': request}) return Response(serializer.data) elif request.method == 'POST': if 'file' not in request.FILES: return Response({'error': 'No file provided'}, status=status.HTTP_400_BAD_REQUEST) file = request.FILES['file'] asset_type = request.POST.get('asset_type', 'unknown') asset_key = request.POST.get('asset_key', 'unknown') # 创建项目素材目录 project_media_dir = os.path.join('projects', str(project.id), asset_type) full_dir = os.path.join(settings.MEDIA_ROOT, project_media_dir) os.makedirs(full_dir, exist_ok=True) # 避免重名覆盖:补上时间戳 from time import time ts = int(time() * 1000) file_name = f"{asset_key}_{ts}_{file.name}" file_path = os.path.join(project_media_dir, file_name) saved_path = default_storage.save(file_path, file) # 获取图片尺寸 width, height = None, None try: img = Image.open(file) width, height = img.size except Exception: pass asset = Asset.objects.create( project=project, asset_type=asset_type, asset_key=asset_key, file_path=saved_path, file_name=file_name, width=width, height=height, ) serializer = AssetSerializer(asset, context={'request': request}) return Response(serializer.data, status=status.HTTP_201_CREATED) @api_view(['GET', 'DELETE']) def asset_detail(request, project_pk, asset_pk): """获取或删除单个素材""" try: project = Project.objects.get(pk=project_pk) asset = project.assets.get(pk=asset_pk) except (Project.DoesNotExist, Asset.DoesNotExist): return Response({'error': 'Asset not found'}, status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': serializer = AssetSerializer(asset, context={'request': request}) return Response(serializer.data) elif request.method == 'DELETE': if asset.file_path: file_full_path = os.path.join(settings.MEDIA_ROOT, asset.file_path) if os.path.exists(file_full_path): try: os.remove(file_full_path) except OSError: pass asset.delete() return Response(status=status.HTTP_204_NO_CONTENT)