274 lines
8.5 KiB
Python
274 lines
8.5 KiB
Python
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')})"
|