lty/qy_lty/card/admin.py
2026-03-17 13:17:02 +08:00

267 lines
9.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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