#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 商店配置管理器 从config_manager.py中提取的商店相关配置管理功能 支持从Excel读取商店配置并与现有JSON配置合并 """ import json import os from pathlib import Path from datetime import datetime try: import pandas as pd PANDAS_AVAILABLE = True except ImportError: PANDAS_AVAILABLE = False print("警告: pandas未安装,无法处理Excel文件") class ShopConfigManager: """商店配置管理器""" def __init__(self, excel_file_path=None, json_file_path=None): """初始化商店配置管理器 Args: excel_file_path: Excel配置文件路径 json_file_path: JSON配置文件路径 """ self.script_dir = Path(__file__).parent # 设置默认路径 if excel_file_path is None: self.excel_file = self.script_dir / "商店配置" / "商店配置表.xlsx" else: self.excel_file = Path(excel_file_path) if json_file_path is None: self.json_file = self.script_dir.parent / "shop.json" else: self.json_file = Path(json_file_path) print(f"Excel文件路径: {self.excel_file}") print(f"JSON文件路径: {self.json_file}") # 商店配置映射 self.shop_mapping = { 'format_type': 'horizontal', 'param_types': { # 每日奖励配置字段 '奖励类型': str, '奖励索引': int, '奖励数量': int, '每日最大领取次数': int, # 英文字段支持 'rewardType': str, 'rewardIndex': int, 'rewardAmount': int, 'maxClaimsPerDay': int } } def load_existing_json_config(self): """加载现有的JSON配置文件""" try: if self.json_file.exists(): with open(self.json_file, 'r', encoding='utf-8') as f: config = json.load(f) print(f"成功加载现有JSON配置") return config else: print(f"JSON文件不存在,将创建新配置: {self.json_file}") return {'dailyRewards': {'money': {'rewards': [], 'maxClaimsPerDay': 36}, 'diamond': {'rewards': [], 'maxClaimsPerDay': 36}}} except Exception as e: print(f"加载JSON配置失败: {e}") return {'dailyRewards': {'money': {'rewards': [], 'maxClaimsPerDay': 36}, 'diamond': {'rewards': [], 'maxClaimsPerDay': 36}}} def read_excel_config(self): """读取Excel配置文件""" if not PANDAS_AVAILABLE: print("错误: pandas未安装,无法读取Excel文件") return None if not self.excel_file.exists(): print(f"Excel文件不存在: {self.excel_file}") return None try: # 读取Excel文件的所有工作表 excel_data = pd.read_excel(self.excel_file, sheet_name=None) print(f"成功读取Excel文件,包含工作表: {list(excel_data.keys())}") config = {'dailyRewards': {'money': {'rewards': [], 'maxClaimsPerDay': 36}, 'diamond': {'rewards': [], 'maxClaimsPerDay': 36}}} # 处理金币奖励工作表 if 'money_rewards' in excel_data or '金币奖励' in excel_data: sheet_name = 'money_rewards' if 'money_rewards' in excel_data else '金币奖励' money_df = excel_data[sheet_name] money_rewards = self.parse_rewards_sheet(money_df, 'money') if money_rewards: config['dailyRewards']['money'] = money_rewards # 处理钻石奖励工作表 if 'diamond_rewards' in excel_data or '钻石奖励' in excel_data: sheet_name = 'diamond_rewards' if 'diamond_rewards' in excel_data else '钻石奖励' diamond_df = excel_data[sheet_name] diamond_rewards = self.parse_rewards_sheet(diamond_df, 'diamond') if diamond_rewards: config['dailyRewards']['diamond'] = diamond_rewards # 如果只有一个工作表,尝试解析为通用格式 if len(excel_data) == 1: sheet_name = list(excel_data.keys())[0] df = excel_data[sheet_name] parsed_config = self.parse_general_shop_sheet(df) if parsed_config: config = parsed_config return config except Exception as e: print(f"读取Excel文件失败: {e}") return None def parse_rewards_sheet(self, df, reward_type): """解析奖励工作表""" try: rewards = [] max_claims = 10 # 默认值 # 读取所有数据行(pandas已经自动处理了标题行) if len(df) > 0: # 从第一行开始读取奖励数据(pandas自动将标题行作为列名) for index in range(0, len(df)): row = df.iloc[index] if pd.isna(row.iloc[0]): continue try: reward_amount = int(row.iloc[0]) rewards.append(reward_amount) except (ValueError, TypeError): continue # 检查是否有第三列的maxClaimsPerDay配置 if len(df.columns) >= 3 and len(df) > 1: try: # 从第一行第三列读取maxClaimsPerDay值(索引0是第一行数据) max_claims_value = df.iloc[0, 2] if not pd.isna(max_claims_value): max_claims = int(max_claims_value) except (ValueError, TypeError, IndexError): pass print(f"解析{reward_type}奖励: {len(rewards)}个奖励, maxClaimsPerDay: {max_claims}") return { 'rewards': rewards, 'maxClaimsPerDay': max_claims } except Exception as e: print(f"解析{reward_type}奖励工作表失败: {e}") return None def parse_general_shop_sheet(self, df): """解析通用商店配置工作表""" try: config = {'dailyRewards': {'money': {'rewards': [], 'maxClaimsPerDay': 36}, 'diamond': {'rewards': [], 'maxClaimsPerDay': 36}}} # 检查列名 columns = [str(col).lower() for col in df.columns] for index, row in df.iterrows(): if pd.isna(row.iloc[0]): continue try: # 根据列名判断奖励类型 reward_type = None reward_amount = None max_claims = 36 # 查找奖励类型列 for i, col in enumerate(columns): if 'type' in col or '类型' in col: reward_type = str(row.iloc[i]).lower() break # 查找奖励数量列 for i, col in enumerate(columns): if 'amount' in col or 'reward' in col or '数量' in col or '奖励' in col: reward_amount = int(row.iloc[i]) break # 查找最大领取次数列 for i, col in enumerate(columns): if 'max' in col or '最大' in col: max_claims = int(row.iloc[i]) break # 添加到对应的奖励类型 if reward_type and reward_amount is not None: if 'money' in reward_type or '金币' in reward_type: config['dailyRewards']['money']['rewards'].append(reward_amount) config['dailyRewards']['money']['maxClaimsPerDay'] = max_claims elif 'diamond' in reward_type or '钻石' in reward_type: config['dailyRewards']['diamond']['rewards'].append(reward_amount) config['dailyRewards']['diamond']['maxClaimsPerDay'] = max_claims except Exception as e: print(f"解析第 {index+1} 行失败: {e}") continue return config except Exception as e: print(f"解析通用商店配置工作表失败: {e}") return None def merge_configs(self, excel_config, json_config): """合并Excel配置和现有JSON配置""" if not excel_config: return json_config try: # 合并每日奖励配置 if 'dailyRewards' in excel_config: if 'dailyRewards' not in json_config: json_config['dailyRewards'] = {} # 合并金币奖励 if 'money' in excel_config['dailyRewards']: json_config['dailyRewards']['money'] = excel_config['dailyRewards']['money'] # 合并钻石奖励 if 'diamond' in excel_config['dailyRewards']: json_config['dailyRewards']['diamond'] = excel_config['dailyRewards']['diamond'] print("配置合并完成") return json_config except Exception as e: print(f"合并配置失败: {e}") return json_config def validate_config(self, config): """验证配置数据""" errors = [] # 验证每日奖励配置 if not config.get("dailyRewards"): errors.append("每日奖励配置为空") else: daily_rewards = config["dailyRewards"] # 验证金币奖励 if "money" in daily_rewards: money_config = daily_rewards["money"] if not money_config.get("rewards"): errors.append("金币奖励列表为空") elif not isinstance(money_config["rewards"], list): errors.append("金币奖励必须是数组") else: for i, reward in enumerate(money_config["rewards"]): if not isinstance(reward, (int, float)) or reward <= 0: errors.append(f"金币奖励第 {i+1} 项必须是正数") if money_config.get("maxClaimsPerDay", 0) <= 0: errors.append("金币每日最大领取次数必须大于0") # 验证钻石奖励 if "diamond" in daily_rewards: diamond_config = daily_rewards["diamond"] if not diamond_config.get("rewards"): errors.append("钻石奖励列表为空") elif not isinstance(diamond_config["rewards"], list): errors.append("钻石奖励必须是数组") else: for i, reward in enumerate(diamond_config["rewards"]): if not isinstance(reward, (int, float)) or reward <= 0: errors.append(f"钻石奖励第 {i+1} 项必须是正数") if diamond_config.get("maxClaimsPerDay", 0) <= 0: errors.append("钻石每日最大领取次数必须大于0") return errors def backup_json(self): """备份当前JSON文件(已禁用)""" # 不再创建备份文件,直接返回成功 return True def save_json_config(self, config): """保存配置到JSON文件""" try: # 确保目录存在 self.json_file.parent.mkdir(parents=True, exist_ok=True) with open(self.json_file, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=2) print(f"配置已保存到: {self.json_file}") return True except Exception as e: print(f"保存JSON文件失败: {e}") return False def import_config(self): """导入配置的主要方法""" print("开始导入商店配置...") # 1. 加载现有JSON配置 json_config = self.load_existing_json_config() # 2. 读取Excel配置 excel_config = self.read_excel_config() if not excel_config: print("无法读取Excel配置,导入失败") return False # 3. 合并配置 merged_config = self.merge_configs(excel_config, json_config) # 4. 验证配置 errors = self.validate_config(merged_config) if errors: print("配置验证失败:") for error in errors: print(f" - {error}") return False # 5. 备份现有配置 if not self.backup_json(): print("备份失败,但继续导入...") # 6. 保存新配置 if self.save_json_config(merged_config): print("商店配置导入成功!") return True else: print("保存配置失败") return False def create_excel_template(self): """创建Excel模板文件""" if not PANDAS_AVAILABLE: print("错误: pandas未安装,无法创建Excel模板") return False try: # 确保目录存在 template_dir = self.excel_file.parent template_dir.mkdir(parents=True, exist_ok=True) # 创建示例数据 money_data = { '奖励数量': [100, 120, 150, 180, 200, 250, 300, 350, 400, 450], '备注': ['第1天', '第2天', '第3天', '第4天', '第5天', '第6天', '第7天', '第8天', '第9天', '第10天'] } diamond_data = { '奖励数量': [10, 12, 15, 18, 20, 25, 30, 35, 40, 45], '备注': ['第1天', '第2天', '第3天', '第4天', '第5天', '第6天', '第7天', '第8天', '第9天', '第10天'] } # 创建DataFrame money_df = pd.DataFrame(money_data) diamond_df = pd.DataFrame(diamond_data) # 保存到Excel文件 with pd.ExcelWriter(self.excel_file, engine='openpyxl') as writer: money_df.to_excel(writer, sheet_name='金币奖励', index=False) diamond_df.to_excel(writer, sheet_name='钻石奖励', index=False) print(f"Excel模板已创建: {self.excel_file}") return True except Exception as e: print(f"创建Excel模板失败: {e}") return False # 测试代码 if __name__ == "__main__": manager = ShopConfigManager() # 创建模板 print("创建Excel模板...") manager.create_excel_template() # 导入配置 print("\n导入配置...") success = manager.import_config() if success: print("\n商店配置管理器测试完成!") else: print("\n商店配置管理器测试失败!")