|
|
@@ -0,0 +1,285 @@
|
|
|
+#!/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()
|