"""P1-10 默认数据 seed 命令 用法: python manage.py seed_affinity # 写入默认数据,已存在则跳过 python manage.py seed_affinity --force # 强制覆盖已存在的默认规则/等级 写入内容(与「好感度系统功能与规则设计.md」§4.2 / §6.2 一致): 1. AffinitySetting 单例(如不存在) 2. 8 条默认互动规则 3. 5 个默认等级 幂等性: 默认按 rule_key(规则)/ level(等级)查询,已存在则跳过。 --force 模式下覆盖已存在记录的字段(不删旧记录)。 """ from django.core.management.base import BaseCommand from django.db import transaction from userapp.models import ( AffinityRule, AffinityLevel, AffinitySetting, ) # 默认规则,与设计文档 §4.2 一致 DEFAULT_RULES = [ { 'rule_key': 'card', 'name': '使用卡片', 'description': '用户使用洛天依卡片', 'trigger_type': 'action', 'min_change': 1, 'max_change': 3, 'single_cap': 3, 'daily_cap': 10, 'cooldown_seconds': 0, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'chat', 'name': '对话', 'description': '与洛天依进行对话', 'trigger_type': 'action', 'min_change': 1, 'max_change': 5, 'single_cap': 5, 'daily_cap': 15, 'cooldown_seconds': 30, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'feed', 'name': '喂食', 'description': '给洛天依喂食', 'trigger_type': 'action', 'min_change': 2, 'max_change': 8, 'single_cap': 8, 'daily_cap': 16, 'cooldown_seconds': 0, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'touch', 'name': '抚摸', 'description': '抚摸洛天依', 'trigger_type': 'action', 'min_change': 1, 'max_change': 3, 'single_cap': 3, 'daily_cap': 9, 'cooldown_seconds': 10, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'dress', 'name': '换装', 'description': '为洛天依更换服装', 'trigger_type': 'action', 'min_change': 2, 'max_change': 6, 'single_cap': 6, 'daily_cap': 12, 'cooldown_seconds': 0, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'prop', 'name': '使用道具', 'description': '使用互动道具', 'trigger_type': 'action', 'min_change': 1, 'max_change': 4, 'single_cap': 4, 'daily_cap': 12, 'cooldown_seconds': 0, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'gift', 'name': '送礼物', 'description': '赠送礼物给洛天依', 'trigger_type': 'action', 'min_change': 5, 'max_change': 15, 'single_cap': 15, 'daily_cap': 20, 'cooldown_seconds': 0, 'is_negative': False, 'is_enabled': True, }, { 'rule_key': 'decay', 'name': '无互动衰减', 'description': '长时间不互动导致好感度下降', 'trigger_type': 'decay', 'min_change': -3, 'max_change': -1, 'single_cap': 3, 'daily_cap': 5, 'cooldown_seconds': 0, 'is_negative': True, 'is_enabled': True, }, ] # 默认等级,与设计文档 §6.2 一致 DEFAULT_LEVELS = [ { 'level': 1, 'name': '初识', 'min_affinity': 0, 'max_affinity': 20, 'unlock_content': '基础对话功能', 'reward_type': 'unlock', 'reward_currency': 0, 'reward_items': [], 'is_enabled': True, }, { 'level': 2, 'name': '相识', 'min_affinity': 21, 'max_affinity': 40, 'unlock_content': '基础服装、道具使用', 'reward_type': 'unlock', 'reward_currency': 0, 'reward_items': [], 'is_enabled': True, }, { 'level': 3, 'name': '熟悉', 'min_affinity': 41, 'max_affinity': 60, 'unlock_content': '更多服装、特殊对话', 'reward_type': 'unlock', 'reward_currency': 0, 'reward_items': [], 'is_enabled': True, }, { 'level': 4, 'name': '亲密', 'min_affinity': 61, 'max_affinity': 80, 'unlock_content': '限定服装、特殊互动', 'reward_type': 'unlock', 'reward_currency': 0, 'reward_items': [], 'is_enabled': True, }, { 'level': 5, 'name': '挚友', 'min_affinity': 81, 'max_affinity': 100, 'unlock_content': '专属内容、特殊剧情', 'reward_type': 'unlock', 'reward_currency': 0, 'reward_items': [], 'is_enabled': True, }, ] class Command(BaseCommand): help = '初始化好感度系统默认数据(AffinitySetting / AffinityRule / AffinityLevel)' def add_arguments(self, parser): parser.add_argument( '--force', action='store_true', help='强制覆盖已存在记录的字段(不删旧记录)', ) @transaction.atomic def handle(self, *args, **options): force = options['force'] self._seed_setting() rules_created, rules_updated = self._seed_rules(force) levels_created, levels_updated = self._seed_levels(force) self.stdout.write(self.style.SUCCESS( f'\n[seed_affinity] 完成:' f'规则 创建 {rules_created} 更新 {rules_updated},' f'等级 创建 {levels_created} 更新 {levels_updated}' )) def _seed_setting(self): if AffinitySetting.objects.exists(): self.stdout.write('AffinitySetting 已存在,跳过') return AffinitySetting.objects.create() self.stdout.write(self.style.SUCCESS('AffinitySetting 已创建(默认值)')) def _seed_rules(self, force): created = 0 updated = 0 for spec in DEFAULT_RULES: rule_key = spec['rule_key'] existing = AffinityRule.objects.filter(rule_key=rule_key).first() if existing is None: AffinityRule.objects.create(**spec) created += 1 self.stdout.write(f' + 规则 {rule_key} 已创建') elif force: for k, v in spec.items(): setattr(existing, k, v) existing.save() updated += 1 self.stdout.write(f' ~ 规则 {rule_key} 已覆盖') else: self.stdout.write(f' - 规则 {rule_key} 已存在,跳过') return created, updated def _seed_levels(self, force): created = 0 updated = 0 for spec in DEFAULT_LEVELS: level_num = spec['level'] existing = AffinityLevel.objects.filter(level=level_num).first() if existing is None: AffinityLevel.objects.create(**spec) created += 1 self.stdout.write(f' + 等级 Lv{level_num} 已创建') elif force: for k, v in spec.items(): setattr(existing, k, v) existing.save() updated += 1 self.stdout.write(f' ~ 等级 Lv{level_num} 已覆盖') else: self.stdout.write(f' - 等级 Lv{level_num} 已存在,跳过') return created, updated