from django.db import models from django.utils.translation import gettext_lazy as _ from django.utils import timezone class Food(models.Model): """ 食物模型 存储各种食物的基本信息、多媒体资源等 """ # 食物类型选择 TYPE_CHOICES = [ ('fruit', '水果'), ('vegetable', '蔬菜'), ('meat', '肉类'), ('seafood', '海鲜'), ('dairy', '乳制品'), ('grain', '谷物'), ('snack', '零食'), ('drink', '饮品'), ('dessert', '甜品'), ('spice', '调料'), ('other', '其他'), ] # 稀有程度选择(参考成就和卡片系统的稀有度设计) RARITY_CHOICES = [ ('common', '普通'), ('uncommon', '不常见'), ('rare', '稀有'), ('epic', '史诗'), ('legendary', '传说'), ('mythic', '神话'), ] # 状态选择 STATUS_CHOICES = [ ('draft', '草稿'), ('published', '已发布'), ('archived', '已归档'), ] # 基本信息 name = models.CharField('食物名称', max_length=100) food_type = models.CharField('食物类型', max_length=20, choices=TYPE_CHOICES) description = models.TextField('食物描述', blank=True, null=True) # 稀有程度 rarity = models.CharField('稀有程度', max_length=20, choices=RARITY_CHOICES, default='common') # 多媒体资源 image = models.URLField( '食物图片', max_length=500, blank=True, null=True, help_text='食物的展示图片URL,通过 /api/common/upload/ 接口上传获取' ) animation_file = models.URLField( '动画文件', max_length=500, blank=True, null=True, help_text='食物的动画文件URL,支持gif/mp4等格式,通过 /api/common/upload/ 接口上传获取' ) sound_effect = models.URLField( '音效文件', max_length=500, blank=True, null=True, help_text='食物相关的音效文件URL,如咀嚼声、烹饪声等,通过 /api/common/upload/ 接口上传获取' ) # 额外属性 calories = models.PositiveIntegerField('卡路里', blank=True, null=True, help_text='每100g的卡路里') taste_tags = models.CharField('口味标签', max_length=200, blank=True, null=True, help_text='用逗号分隔,如:甜,酸,脆') cooking_methods = models.TextField('烹饪方法', blank=True, null=True) nutritional_value = models.TextField('营养价值', blank=True, null=True) # 游戏相关属性 effect_description = models.TextField('效果描述', blank=True, null=True, help_text='在游戏中的特殊效果') boost_attributes = models.JSONField('属性加成', blank=True, null=True, help_text='JSON格式的属性加成数据') # 获取和使用 unlock_condition = models.TextField('解锁条件', blank=True, null=True) usage_limit = models.PositiveIntegerField('使用次数限制', blank=True, null=True, help_text='0表示无限制') # 状态和时间 status = models.CharField('状态', max_length=20, choices=STATUS_CHOICES, default='draft') is_limited = models.BooleanField('限时食物', default=False) available_from = models.DateTimeField('开始时间', blank=True, null=True) available_until = models.DateTimeField('结束时间', blank=True, null=True) # 时间戳 created_at = models.DateTimeField('创建时间', auto_now_add=True) updated_at = models.DateTimeField('更新时间', auto_now=True) published_at = models.DateTimeField('发布时间', blank=True, null=True) def publish(self): """发布食物""" self.status = 'published' self.published_at = timezone.now() self.save() def archive(self): """归档食物""" self.status = 'archived' self.save() def is_available(self): """检查食物是否可用""" if self.status != 'published': return False if not self.is_limited: return True now = timezone.now() if self.available_from and now < self.available_from: return False if self.available_until and now > self.available_until: return False return True def get_taste_tags_list(self): """获取口味标签列表""" if self.taste_tags: return [tag.strip() for tag in self.taste_tags.split(',') if tag.strip()] return [] class Meta: verbose_name = '食物' verbose_name_plural = '食物' ordering = ['-created_at'] indexes = [ models.Index(fields=['name']), models.Index(fields=['food_type']), models.Index(fields=['rarity']), models.Index(fields=['status']), models.Index(fields=['created_at']), ] def __str__(self): return f"{self.name} ({self.get_rarity_display()})" class UserFood(models.Model): """ 用户食物关联模型 记录用户拥有的食物及使用情况 """ from userapp.models import ParadiseUser user = models.ForeignKey( ParadiseUser, on_delete=models.CASCADE, related_name='foods', verbose_name='用户' ) food = models.ForeignKey( Food, on_delete=models.CASCADE, related_name='users', verbose_name='食物' ) # 拥有数量和使用记录 quantity = models.PositiveIntegerField('拥有数量', default=1) used_count = models.PositiveIntegerField('已使用次数', default=0) # 获得信息 obtained_at = models.DateTimeField('获得时间', auto_now_add=True) obtained_method = models.CharField( '获得方式', max_length=50, blank=True, null=True, help_text='如:购买、奖励、活动等' ) # 最后使用时间 last_used_at = models.DateTimeField('最后使用时间', blank=True, null=True) class Meta: verbose_name = '用户食物' verbose_name_plural = '用户食物' ordering = ['-obtained_at'] unique_together = ['user', 'food'] indexes = [ models.Index(fields=['user']), models.Index(fields=['food']), models.Index(fields=['obtained_at']), ] def __str__(self): return f"{self.user.username} - {self.food.name} (x{self.quantity})" def can_use(self): """检查是否可以使用""" if self.quantity <= 0: return False if self.food.usage_limit and self.used_count >= self.food.usage_limit: return False return self.food.is_available() def use_food(self): """使用食物""" if not self.can_use(): return False self.used_count += 1 if self.food.usage_limit and self.used_count >= self.food.usage_limit: self.quantity = 0 else: self.quantity = max(0, self.quantity - 1) self.last_used_at = timezone.now() self.save() return True class FoodUsageLog(models.Model): """ 食物使用记录 记录食物的详细使用日志 """ from userapp.models import ParadiseUser user = models.ForeignKey( ParadiseUser, on_delete=models.CASCADE, related_name='food_usage_logs', verbose_name='用户' ) food = models.ForeignKey( Food, on_delete=models.CASCADE, related_name='usage_logs', verbose_name='食物' ) # 使用信息 used_at = models.DateTimeField('使用时间', auto_now_add=True) effect_applied = models.JSONField('应用效果', blank=True, null=True, help_text='JSON格式的效果数据') notes = models.TextField('备注', blank=True, null=True) # 使用环境 usage_context = models.CharField( '使用场景', max_length=100, blank=True, null=True, help_text='如:战斗中、休息时、特殊活动等' ) class Meta: verbose_name = '食物使用记录' verbose_name_plural = '食物使用记录' ordering = ['-used_at'] indexes = [ models.Index(fields=['user']), models.Index(fields=['food']), models.Index(fields=['used_at']), ] def __str__(self): return f"{self.user.username} 使用了 {self.food.name} ({self.used_at.strftime('%Y-%m-%d %H:%M')})"