| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- #!/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}")
-
- # 解析其他配置项(如果需要)
- 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()
|