#!/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 BallPriceConfigManager: """小球价格配置管理器""" 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 / "ball_price_config.json" else: self.json_file = Path(json_file_path) print(f"Excel文件路径: {self.excel_file}") print(f"JSON文件路径: {self.json_file}") 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 {} except Exception as e: print(f"加载JSON配置失败: {e}") return {} 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_sheet = None sheet_names = ['小球价格配置', 'Ball Price Config', 'ball_price', '价格配置'] for sheet_name in sheet_names: if sheet_name in excel_data: config_sheet = excel_data[sheet_name] print(f"找到小球价格配置工作表: {sheet_name}") break if config_sheet is None: # 如果没有找到特定工作表,使用第一个工作表 first_sheet_name = list(excel_data.keys())[0] config_sheet = excel_data[first_sheet_name] print(f"未找到指定工作表,使用第一个工作表: {first_sheet_name}") return self.parse_ball_price_config(config_sheet) except Exception as e: print(f"读取Excel文件失败: {e}") return None def parse_ball_price_config(self, df): """解析小球价格配置数据""" try: print(f"开始解析配置数据,数据形状: {df.shape}") print(f"列名: {list(df.columns)}") config = {} # 处理纵向格式的配置表(配置项在第一列,数值在第二列) # 创建配置项到数值的映射 config_map = {} # 假设第一列是配置项,第二列是数值 first_col = df.columns[0] if len(df.columns) > 0 else None second_col = df.columns[1] if len(df.columns) > 1 else None if first_col is None or second_col is None: print("Excel文件格式不正确,需要至少两列数据") return None print(f"配置项列: {first_col}, 数值列: {second_col}") # 遍历所有行,建立配置项到数值的映射 for index, row in df.iterrows(): config_item = row[first_col] config_value = row[second_col] if pd.notna(config_item) and pd.notna(config_value): config_item_str = str(config_item).strip() # 尝试转换数值类型 try: if isinstance(config_value, (int, float)): config_map[config_item_str] = config_value else: # 尝试转换为数字 config_value_str = str(config_value).strip() if config_value_str.isdigit(): config_map[config_item_str] = int(config_value_str) elif config_value_str.replace('.', '', 1).isdigit(): config_map[config_item_str] = float(config_value_str) else: config_map[config_item_str] = config_value_str except: config_map[config_item_str] = config_value print(f"配置项: {config_item_str} = {config_map[config_item_str]}") print(f"配置映射: {config_map}") # 解析添加小球配置 add_ball_config = {} if '增加小球初始价格' in config_map: add_ball_config['initialPrice'] = int(config_map['增加小球初始价格']) if '增加小球价格增量' in config_map: add_ball_config['priceIncrement'] = int(config_map['增加小球价格增量']) if '增加小球最大价格' in config_map: add_ball_config['maxPrice'] = int(config_map['增加小球最大价格']) # 添加当前价格和购买次数(如果存在) if '增加小球当前价格' in config_map: add_ball_config['currentPrice'] = int(config_map['增加小球当前价格']) if '增加小球购买次数' in config_map: add_ball_config['purchaseCount'] = int(config_map['增加小球购买次数']) # 添加描述 add_ball_config['description'] = "添加小球的价格配置" if add_ball_config: config['addBallPricing'] = add_ball_config print(f"解析添加小球配置: {add_ball_config}") # 解析刷新方块配置 refresh_block_config = {} if '刷新方块初始价格' in config_map: refresh_block_config['initialPrice'] = int(config_map['刷新方块初始价格']) if '刷新方块价格增量' in config_map: refresh_block_config['priceIncrement'] = int(config_map['刷新方块价格增量']) if '刷新方块最大价格' in config_map: refresh_block_config['maxPrice'] = int(config_map['刷新方块最大价格']) # 添加当前价格和购买次数(如果存在) if '刷新方块当前价格' in config_map: refresh_block_config['currentPrice'] = int(config_map['刷新方块当前价格']) if '刷新方块购买次数' in config_map: refresh_block_config['purchaseCount'] = int(config_map['刷新方块购买次数']) # 添加描述 refresh_block_config['description'] = "刷新方块的价格配置" if refresh_block_config: config['refreshBlockPricing'] = refresh_block_config print(f"解析刷新方块配置: {refresh_block_config}") # 解析增加金币奖励配置 add_coin_reward_config = {} if '观看广告奖励初始价格' in config_map: add_coin_reward_config['initialPrice'] = int(config_map['观看广告奖励初始价格']) if '观看广告奖励价格增量' in config_map: add_coin_reward_config['priceIncrement'] = int(config_map['观看广告奖励价格增量']) if '观看广告奖励最大价格' in config_map: add_coin_reward_config['maxPrice'] = int(config_map['观看广告奖励最大价格']) # 添加描述 add_coin_reward_config['description'] = "观看广告获得的金币奖励配置" if add_coin_reward_config and 'initialPrice' in add_coin_reward_config: config['addCoinReward'] = add_coin_reward_config print(f"解析增加金币奖励配置: {add_coin_reward_config}") # 解析其他配置项(如果需要) other_config = {} for key, value in config_map.items(): if key in ['restitution', 'safeDistance', 'edgeOffset', 'sensor', 'maxAttempts']: other_config[key] = value if other_config: config['otherSettings'] = other_config print(f"解析其他配置: {other_config}") print(f"最终解析结果: {config}") return config if config else None except Exception as e: import traceback print(f"解析小球价格配置失败: {e}") print(f"详细错误信息: {traceback.format_exc()}") return None def merge_configs(self, existing_config, new_config): """合并现有配置和新配置""" if not new_config: return existing_config merged_config = existing_config.copy() # 合并小球价格配置 for key, value in new_config.items(): merged_config[key] = value print(f"更新配置: {key}") return merged_config def save_config(self, config): """保存配置到JSON文件""" try: # 创建备份 if self.json_file.exists(): backup_path = self.json_file.with_suffix(f'.backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json') with open(self.json_file, 'r', encoding='utf-8') as f: backup_data = f.read() with open(backup_path, 'w', encoding='utf-8') as f: f.write(backup_data) print(f"创建备份文件: {backup_path}") # 保存新配置 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"保存配置失败: {e}") return False def import_config(self, excel_file_path=None, json_file_path=None): """导入配置的主方法""" if excel_file_path: self.excel_file = Path(excel_file_path) if json_file_path: self.json_file = Path(json_file_path) print("开始导入小球价格配置...") # 1. 加载现有配置 existing_config = self.load_existing_json_config() # 2. 读取Excel配置 new_config = self.read_excel_config() if new_config is None: print("读取Excel配置失败,导入终止") return False # 3. 合并配置 merged_config = self.merge_configs(existing_config, new_config) # 4. 保存配置 success = self.save_config(merged_config) if success: print("小球价格配置导入成功!") else: print("小球价格配置导入失败!") return success def main(): """测试函数""" manager = BallPriceConfigManager() manager.import_config() if __name__ == "__main__": main()