from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from django.conf import settings from .models import LibraryAsset, Project, Asset from .serializers import LibraryAssetSerializer import os @api_view(['GET']) def library_list(request): """列出所有预设素材(按主题分组)""" assets = LibraryAsset.objects.all() serializer = LibraryAssetSerializer(assets, many=True, context={'request': request}) # 按 theme_id 分组 grouped = {} for a in serializer.data: a['file_url'] = f'{settings.MEDIA_URL}{a["file_path"]}' grouped.setdefault(a['theme_id'], { 'theme_id': a['theme_id'], 'theme_name': a['theme_name'], 'description': a['description'], 'items': [], })['items'].append(a) return Response(list(grouped.values())) @api_view(['GET']) def library_themes(request): """返回所有主题的元信息(不含具体素材项,用于渲染主题筛选器)""" themes = LibraryAsset.objects.values('theme_id', 'theme_name', 'description').distinct() return Response(list(themes)) @api_view(['GET']) def library_detail(request, pk): """单个预设素材的详情""" try: a = LibraryAsset.objects.get(pk=pk) except LibraryAsset.DoesNotExist: return Response({'error': 'Not found'}, status=status.HTTP_404_NOT_FOUND) data = LibraryAssetSerializer(a, context={'request': request}).data data['file_url'] = f'{settings.MEDIA_URL}{data["file_path"]}' return Response(data) @api_view(['POST']) def library_apply(request, project_pk, pk): """把预设素材应用到项目某张牌(默认:spade-J / joker-big 等) 请求体: { card_key?: 'spade-J' } 可选;不传则用预设素材的 role + 默认 spade """ try: project = Project.objects.get(pk=project_pk) lib = LibraryAsset.objects.get(pk=pk) except (Project.DoesNotExist, LibraryAsset.DoesNotExist): return Response({'error': 'not found'}, status=status.HTTP_404_NOT_FOUND) card_key = request.data.get('card_key') if not card_key: # 默认: spade-{role} 或 joker-{which} if lib.role == 'joker': card_key = 'joker-big' # 默认应用到 big;用户可换 else: card_key = f'spade-{lib.role}' # 决定 asset_type: J/Q/K -> face_card;joker -> joker asset_type = 'face_card' if lib.role in ('J', 'Q', 'K') else 'joker' asset_key = card_key # 删除该项目同 (asset_type, asset_key) 的旧记录(避免重复) Asset.objects.filter(project=project, asset_type=asset_type, asset_key=asset_key).delete() # 复制 library 文件到 projects// 下,避免污染原文件 import shutil from time import time 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) src_path = os.path.join(settings.MEDIA_ROOT, lib.file_path) ts = int(time() * 1000) new_file_name = f'{asset_key}_{ts}_{lib.file_name}' dst_rel = os.path.join(project_media_dir, new_file_name) dst_abs = os.path.join(settings.MEDIA_ROOT, dst_rel) shutil.copy2(src_path, dst_abs) # 读 svg 尺寸 width = height = None try: from PIL import Image with Image.open(dst_abs) as im: width, height = im.size except Exception: pass asset = Asset.objects.create( project=project, asset_type=asset_type, asset_key=asset_key, file_path=dst_rel, file_name=new_file_name, width=width, height=height, ) return Response({ 'ok': True, 'asset_id': asset.id, 'card_key': card_key, 'asset_type': asset_type, 'file_url': f'{settings.MEDIA_URL}{dst_rel}', }, status=status.HTTP_201_CREATED)