115 lines
3.9 KiB
Python
115 lines
3.9 KiB
Python
|
|
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/<pid>/ 下,避免污染原文件
|
|||
|
|
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)
|