ball_price_config_manager.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 小球价格配置管理器
  5. 从config_manager.py中提取的小球价格相关配置管理功能
  6. 支持从Excel读取小球价格配置并与现有JSON配置合并
  7. """
  8. import json
  9. import os
  10. from pathlib import Path
  11. from datetime import datetime
  12. try:
  13. import pandas as pd
  14. PANDAS_AVAILABLE = True
  15. except ImportError:
  16. PANDAS_AVAILABLE = False
  17. print("警告: pandas未安装,无法处理Excel文件")
  18. class BallPriceConfigManager:
  19. """小球价格配置管理器"""
  20. def __init__(self, excel_file_path=None, json_file_path=None):
  21. """初始化小球价格配置管理器
  22. Args:
  23. excel_file_path: Excel配置文件路径
  24. json_file_path: JSON配置文件路径
  25. """
  26. self.script_dir = Path(__file__).parent
  27. # 设置默认路径
  28. if excel_file_path is None:
  29. self.excel_file = self.script_dir / "小球价格配置" / "小球价格配置表.xlsx"
  30. else:
  31. self.excel_file = Path(excel_file_path)
  32. if json_file_path is None:
  33. self.json_file = self.script_dir.parent / "ball_price_config.json"
  34. else:
  35. self.json_file = Path(json_file_path)
  36. print(f"Excel文件路径: {self.excel_file}")
  37. print(f"JSON文件路径: {self.json_file}")
  38. def load_existing_json_config(self):
  39. """加载现有的JSON配置文件"""
  40. try:
  41. if self.json_file.exists():
  42. with open(self.json_file, 'r', encoding='utf-8') as f:
  43. config = json.load(f)
  44. print(f"成功加载现有JSON配置")
  45. return config
  46. else:
  47. print(f"JSON文件不存在,将创建新配置: {self.json_file}")
  48. return {}
  49. except Exception as e:
  50. print(f"加载JSON配置失败: {e}")
  51. return {}
  52. def read_excel_config(self):
  53. """读取Excel配置文件"""
  54. if not PANDAS_AVAILABLE:
  55. print("错误: pandas未安装,无法读取Excel文件")
  56. return None
  57. if not self.excel_file.exists():
  58. print(f"Excel文件不存在: {self.excel_file}")
  59. return None
  60. try:
  61. # 读取Excel文件
  62. excel_data = pd.read_excel(self.excel_file, sheet_name=None)
  63. print(f"成功读取Excel文件,包含工作表: {list(excel_data.keys())}")
  64. # 查找小球价格配置工作表
  65. config_sheet = None
  66. sheet_names = ['小球价格配置', 'Ball Price Config', 'ball_price', '价格配置']
  67. for sheet_name in sheet_names:
  68. if sheet_name in excel_data:
  69. config_sheet = excel_data[sheet_name]
  70. print(f"找到小球价格配置工作表: {sheet_name}")
  71. break
  72. if config_sheet is None:
  73. # 如果没有找到特定工作表,使用第一个工作表
  74. first_sheet_name = list(excel_data.keys())[0]
  75. config_sheet = excel_data[first_sheet_name]
  76. print(f"未找到指定工作表,使用第一个工作表: {first_sheet_name}")
  77. return self.parse_ball_price_config(config_sheet)
  78. except Exception as e:
  79. print(f"读取Excel文件失败: {e}")
  80. return None
  81. def parse_ball_price_config(self, df):
  82. """解析小球价格配置数据"""
  83. try:
  84. print(f"开始解析配置数据,数据形状: {df.shape}")
  85. print(f"列名: {list(df.columns)}")
  86. config = {}
  87. # 处理纵向格式的配置表(配置项在第一列,数值在第二列)
  88. # 创建配置项到数值的映射
  89. config_map = {}
  90. # 假设第一列是配置项,第二列是数值
  91. first_col = df.columns[0] if len(df.columns) > 0 else None
  92. second_col = df.columns[1] if len(df.columns) > 1 else None
  93. if first_col is None or second_col is None:
  94. print("Excel文件格式不正确,需要至少两列数据")
  95. return None
  96. print(f"配置项列: {first_col}, 数值列: {second_col}")
  97. # 遍历所有行,建立配置项到数值的映射
  98. for index, row in df.iterrows():
  99. config_item = row[first_col]
  100. config_value = row[second_col]
  101. if pd.notna(config_item) and pd.notna(config_value):
  102. config_item_str = str(config_item).strip()
  103. # 尝试转换数值类型
  104. try:
  105. if isinstance(config_value, (int, float)):
  106. config_map[config_item_str] = config_value
  107. else:
  108. # 尝试转换为数字
  109. config_value_str = str(config_value).strip()
  110. if config_value_str.isdigit():
  111. config_map[config_item_str] = int(config_value_str)
  112. elif config_value_str.replace('.', '', 1).isdigit():
  113. config_map[config_item_str] = float(config_value_str)
  114. else:
  115. config_map[config_item_str] = config_value_str
  116. except:
  117. config_map[config_item_str] = config_value
  118. print(f"配置项: {config_item_str} = {config_map[config_item_str]}")
  119. print(f"配置映射: {config_map}")
  120. # 解析添加小球配置
  121. add_ball_config = {}
  122. if '增加小球初始价格' in config_map:
  123. add_ball_config['initialPrice'] = int(config_map['增加小球初始价格'])
  124. if '增加小球价格增量' in config_map:
  125. add_ball_config['priceIncrement'] = int(config_map['增加小球价格增量'])
  126. if '增加小球最大价格' in config_map:
  127. add_ball_config['maxPrice'] = int(config_map['增加小球最大价格'])
  128. # 添加当前价格和购买次数(如果存在)
  129. if '增加小球当前价格' in config_map:
  130. add_ball_config['currentPrice'] = int(config_map['增加小球当前价格'])
  131. if '增加小球购买次数' in config_map:
  132. add_ball_config['purchaseCount'] = int(config_map['增加小球购买次数'])
  133. # 添加描述
  134. add_ball_config['description'] = "添加小球的价格配置"
  135. if add_ball_config:
  136. config['addBallPricing'] = add_ball_config
  137. print(f"解析添加小球配置: {add_ball_config}")
  138. # 解析刷新方块配置
  139. refresh_block_config = {}
  140. if '刷新方块初始价格' in config_map:
  141. refresh_block_config['initialPrice'] = int(config_map['刷新方块初始价格'])
  142. if '刷新方块价格增量' in config_map:
  143. refresh_block_config['priceIncrement'] = int(config_map['刷新方块价格增量'])
  144. if '刷新方块最大价格' in config_map:
  145. refresh_block_config['maxPrice'] = int(config_map['刷新方块最大价格'])
  146. # 添加当前价格和购买次数(如果存在)
  147. if '刷新方块当前价格' in config_map:
  148. refresh_block_config['currentPrice'] = int(config_map['刷新方块当前价格'])
  149. if '刷新方块购买次数' in config_map:
  150. refresh_block_config['purchaseCount'] = int(config_map['刷新方块购买次数'])
  151. # 添加描述
  152. refresh_block_config['description'] = "刷新方块的价格配置"
  153. if refresh_block_config:
  154. config['refreshBlockPricing'] = refresh_block_config
  155. print(f"解析刷新方块配置: {refresh_block_config}")
  156. # 解析增加金币奖励配置
  157. add_coin_reward_config = {}
  158. if '观看广告奖励初始价格' in config_map:
  159. add_coin_reward_config['initialPrice'] = int(config_map['观看广告奖励初始价格'])
  160. if '观看广告奖励价格增量' in config_map:
  161. add_coin_reward_config['priceIncrement'] = int(config_map['观看广告奖励价格增量'])
  162. if '观看广告奖励最大价格' in config_map:
  163. add_coin_reward_config['maxPrice'] = int(config_map['观看广告奖励最大价格'])
  164. # 添加描述
  165. add_coin_reward_config['description'] = "观看广告获得的金币奖励配置"
  166. if add_coin_reward_config and 'initialPrice' in add_coin_reward_config:
  167. config['addCoinReward'] = add_coin_reward_config
  168. print(f"解析增加金币奖励配置: {add_coin_reward_config}")
  169. # 解析其他配置项(如果需要)
  170. other_config = {}
  171. for key, value in config_map.items():
  172. if key in ['restitution', 'safeDistance', 'edgeOffset', 'sensor', 'maxAttempts']:
  173. other_config[key] = value
  174. if other_config:
  175. config['otherSettings'] = other_config
  176. print(f"解析其他配置: {other_config}")
  177. print(f"最终解析结果: {config}")
  178. return config if config else None
  179. except Exception as e:
  180. import traceback
  181. print(f"解析小球价格配置失败: {e}")
  182. print(f"详细错误信息: {traceback.format_exc()}")
  183. return None
  184. def merge_configs(self, existing_config, new_config):
  185. """合并现有配置和新配置"""
  186. if not new_config:
  187. return existing_config
  188. merged_config = existing_config.copy()
  189. # 合并小球价格配置
  190. for key, value in new_config.items():
  191. merged_config[key] = value
  192. print(f"更新配置: {key}")
  193. return merged_config
  194. def save_config(self, config):
  195. """保存配置到JSON文件"""
  196. try:
  197. # 创建备份
  198. if self.json_file.exists():
  199. backup_path = self.json_file.with_suffix(f'.backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json')
  200. with open(self.json_file, 'r', encoding='utf-8') as f:
  201. backup_data = f.read()
  202. with open(backup_path, 'w', encoding='utf-8') as f:
  203. f.write(backup_data)
  204. print(f"创建备份文件: {backup_path}")
  205. # 保存新配置
  206. with open(self.json_file, 'w', encoding='utf-8') as f:
  207. json.dump(config, f, ensure_ascii=False, indent=2)
  208. print(f"配置已保存到: {self.json_file}")
  209. return True
  210. except Exception as e:
  211. print(f"保存配置失败: {e}")
  212. return False
  213. def import_config(self, excel_file_path=None, json_file_path=None):
  214. """导入配置的主方法"""
  215. if excel_file_path:
  216. self.excel_file = Path(excel_file_path)
  217. if json_file_path:
  218. self.json_file = Path(json_file_path)
  219. print("开始导入小球价格配置...")
  220. # 1. 加载现有配置
  221. existing_config = self.load_existing_json_config()
  222. # 2. 读取Excel配置
  223. new_config = self.read_excel_config()
  224. if new_config is None:
  225. print("读取Excel配置失败,导入终止")
  226. return False
  227. # 3. 合并配置
  228. merged_config = self.merge_configs(existing_config, new_config)
  229. # 4. 保存配置
  230. success = self.save_config(merged_config)
  231. if success:
  232. print("小球价格配置导入成功!")
  233. else:
  234. print("小球价格配置导入失败!")
  235. return success
  236. def main():
  237. """测试函数"""
  238. manager = BallPriceConfigManager()
  239. manager.import_config()
  240. if __name__ == "__main__":
  241. main()