from django.contrib import admin from django.utils.translation import gettext_lazy as _ from .models import ( CardTemplate, Card, CardBatch, CardUsageLog, ClothingAttributes, PropAttributes, SongAttributes, DanceAttributes, FurnitureAttributes, DecorationAttributes ) from django.utils import timezone import uuid import secrets import string # Register your models here. class ClothingAttributesInline(admin.StackedInline): model = ClothingAttributes can_delete = False verbose_name = '服装属性' verbose_name_plural = '服装属性' class PropAttributesInline(admin.StackedInline): model = PropAttributes can_delete = False verbose_name = '道具属性' verbose_name_plural = '道具属性' class SongAttributesInline(admin.StackedInline): model = SongAttributes can_delete = False verbose_name = '音乐属性' verbose_name_plural = '音乐属性' class DanceAttributesInline(admin.StackedInline): model = DanceAttributes can_delete = False verbose_name = '舞蹈属性' verbose_name_plural = '舞蹈属性' class FurnitureAttributesInline(admin.StackedInline): model = FurnitureAttributes can_delete = False verbose_name = '家具属性' verbose_name_plural = '家具属性' class DecorationAttributesInline(admin.StackedInline): model = DecorationAttributes can_delete = False verbose_name = '装饰属性' verbose_name_plural = '装饰属性' @admin.register(CardTemplate) class CardTemplateAdmin(admin.ModelAdmin): list_display = ('name', 'category', 'rarity', 'card_type', 'status', 'price', 'created_at') list_filter = ('category', 'rarity', 'card_type', 'status') search_fields = ('name', 'description') ordering = ('-created_at',) readonly_fields = ('published_at',) def get_inlines(self, request, obj=None): if obj is None: return [] category_to_inline = { 'clothing': ClothingAttributesInline, 'prop': PropAttributesInline, 'song': SongAttributesInline, 'dance': DanceAttributesInline, 'furniture': FurnitureAttributesInline, 'decoration': DecorationAttributesInline, } if obj.category in category_to_inline: return [category_to_inline[obj.category]] return [] fieldsets = ( ('基本信息', { 'fields': ('name', 'category', 'description') }), ('类型和稀有度', { 'fields': ('card_type', 'rarity') }), ('资源信息', { 'fields': ('image', 'model_url', 'model_version') }), ('发布信息', { 'fields': ('status', 'published_at', 'price') }), ) actions = ['publish_templates', 'archive_templates'] class Meta: verbose_name = '卡片模板' verbose_name_plural = '卡片模板管理' def publish_templates(self, request, queryset): count = 0 for template in queryset.filter(status='draft'): template.publish() count += 1 self.message_user(request, f'成功发布 {count} 个卡片模板') publish_templates.short_description = '发布选中的卡片模板' def archive_templates(self, request, queryset): count = 0 for template in queryset.filter(status='published'): template.archive() count += 1 self.message_user(request, f'成功归档 {count} 个卡片模板') archive_templates.short_description = '归档选中的卡片模板' class CardUsageLogInline(admin.TabularInline): model = CardUsageLog extra = 0 readonly_fields = ('user', 'action', 'details', 'old_status', 'new_status', 'created_at', 'ip_address') can_delete = False max_num = 10 verbose_name = _("使用记录") verbose_name_plural = _("使用记录") def has_add_permission(self, request, obj=None): return False @admin.register(Card) class CardAdmin(admin.ModelAdmin): list_display = ('unique_id', 'name', 'template', 'category', 'status', 'user', 'used_at', 'manufactured') list_filter = ('template', 'category', 'status', 'manufactured') search_fields = ('unique_id', 'name', 'user__username') ordering = ('-created_at',) readonly_fields = ('unique_id', 'manufactured_at', 'used_at', 'batch') inlines = [CardUsageLogInline] fieldsets = ( ('基本信息', { 'fields': ('unique_id', 'template', 'name', 'category', 'description', 'price', 'batch') }), ('状态信息', { 'fields': ('status', 'manufactured', 'manufactured_at', 'user', 'used_at') }), ) class Meta: verbose_name = '卡片' verbose_name_plural = '卡片管理' def save_model(self, request, obj, form, change): # Log changes to the card if change: # If this is an update old_obj = self.model.objects.get(pk=obj.pk) if old_obj.status != obj.status: CardUsageLog.objects.create( card=obj, user=request.user if hasattr(request, 'user') else None, action='update', details=f"状态从 {old_obj.get_status_display()} 变更为 {obj.get_status_display()}", old_status=old_obj.status, new_status=obj.status, ip_address=self.get_client_ip(request) ) else: # If this is a new card # 如果是新卡片且没有unique_id,生成一个 if not obj.unique_id: obj.unique_id = self.generate_unique_id() CardUsageLog.objects.create( card=obj, user=request.user if hasattr(request, 'user') else None, action='create', details="卡片创建", old_status='', new_status=obj.status, ip_address=self.get_client_ip(request) ) super().save_model(request, obj, form, change) def get_client_ip(self, request): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: ip = x_forwarded_for.split(',')[0] else: ip = request.META.get('REMOTE_ADDR') return ip def generate_unique_id(self, length=16): """ 生成一个唯一、不可预测的卡片标识 """ alphabet = string.ascii_letters + string.digits while True: # 生成一个随机字符串 unique_id = ''.join(secrets.choice(alphabet) for _ in range(length)) # 确保唯一 if not Card.objects.filter(unique_id=unique_id).exists(): return unique_id @admin.register(CardBatch) class CardBatchAdmin(admin.ModelAdmin): list_display = ('batch_number', 'template', 'category', 'quantity', 'status', 'exported', 'sent_to_production', 'published') list_filter = ('template', 'category', 'status', 'exported', 'sent_to_production', 'published') search_fields = ('batch_number', 'description') ordering = ('-created_at',) readonly_fields = ('batch_number', 'exported_at', 'production_date', 'published_at') fieldsets = ( ('基本信息', { 'fields': ('batch_number', 'template', 'category', 'quantity', 'description') }), ('状态信息', { 'fields': ('status', 'exported', 'exported_at', 'sent_to_production', 'production_date', 'published', 'published_at', 'excel_file') }), ) actions = ['mark_batches_manufactured', 'publish_batches'] class Meta: verbose_name = '卡片批次' verbose_name_plural = '卡片批次管理' def save_model(self, request, obj, form, change): if not change: # If this is a new batch # Generate batch number if not provided if not obj.batch_number: obj.batch_number = f"B{timezone.now().strftime('%Y%m%d%H%M%S')}" super().save_model(request, obj, form, change) def mark_batches_manufactured(self, request, queryset): count = 0 for batch in queryset.filter(sent_to_production=False, exported=True): batch.mark_produced() count += 1 self.message_user(request, f'成功标记 {count} 个批次为已生产') mark_batches_manufactured.short_description = '标记选中的批次为已生产' def publish_batches(self, request, queryset): count = 0 for batch in queryset.filter(published=False, sent_to_production=True): batch.publish() count += 1 self.message_user(request, f'成功发布 {count} 个批次') publish_batches.short_description = '发布选中的批次' @admin.register(CardUsageLog) class CardUsageLogAdmin(admin.ModelAdmin): list_display = ('card', 'user', 'action', 'created_at', 'ip_address') list_filter = ('action', 'created_at') search_fields = ('card__unique_id', 'user__username', 'ip_address') ordering = ('-created_at',) readonly_fields = ('card', 'user', 'action', 'details', 'old_status', 'new_status', 'ip_address', 'created_at') class Meta: verbose_name = '卡片使用记录' verbose_name_plural = '卡片使用记录管理' def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False