| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- #!/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商店配置管理器测试失败!")
|