skill_config_manager.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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 SkillConfigManager:
  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 / "skill_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. # 默认配置结构
  39. self.default_config = {
  40. "skillTypes": [],
  41. "skillGroups": [],
  42. "totalGroups": 12,
  43. "skillsPerGroup": 3
  44. }
  45. def load_existing_json_config(self):
  46. """加载现有的JSON配置文件"""
  47. try:
  48. if self.json_file.exists():
  49. with open(self.json_file, 'r', encoding='utf-8') as f:
  50. config = json.load(f)
  51. print(f"成功加载现有JSON配置")
  52. return config
  53. else:
  54. print(f"JSON文件不存在,将创建新配置: {self.json_file}")
  55. return self.default_config.copy()
  56. except Exception as e:
  57. print(f"加载JSON配置失败: {e}")
  58. return self.default_config.copy()
  59. def read_excel_config(self):
  60. """读取Excel配置文件"""
  61. if not PANDAS_AVAILABLE:
  62. raise Exception("pandas未安装,无法读取Excel文件")
  63. if not self.excel_file.exists():
  64. raise Exception(f"Excel文件不存在: {self.excel_file}")
  65. try:
  66. # 读取所有工作表
  67. excel_data = pd.read_excel(self.excel_file, sheet_name=None)
  68. print(f"成功读取Excel文件,包含工作表: {list(excel_data.keys())}")
  69. return excel_data
  70. except Exception as e:
  71. raise Exception(f"读取Excel文件失败: {e}")
  72. def parse_skill_types(self, df):
  73. """解析技能类型配置"""
  74. skill_types = []
  75. try:
  76. for index, row in df.iterrows():
  77. # 跳过表头,但允许ID为0的有效数据
  78. if index == 0 and str(row.iloc[0]).strip() in ['ID', 'id', '技能类型ID']:
  79. continue
  80. # 检查是否为有效数据行
  81. if pd.isna(row.iloc[0]) or str(row.iloc[0]).strip() == '':
  82. continue
  83. skill_type = {
  84. "id": int(row.iloc[0]) if pd.notna(row.iloc[0]) else 0,
  85. "name": str(row.iloc[1]).strip() if pd.notna(row.iloc[1]) else "",
  86. "displayName": str(row.iloc[2]).strip() if pd.notna(row.iloc[2]) else "",
  87. "nameTemplate": str(row.iloc[3]).strip() if pd.notna(row.iloc[3]) else "",
  88. "type": str(row.iloc[4]).strip() if pd.notna(row.iloc[4]) else ""
  89. }
  90. skill_types.append(skill_type)
  91. print(f"解析到 {len(skill_types)} 个技能类型")
  92. return skill_types
  93. except Exception as e:
  94. print(f"解析技能类型失败: {e}")
  95. return []
  96. def parse_skill_groups(self, df):
  97. """解析技能组配置"""
  98. skill_groups = []
  99. try:
  100. for index, row in df.iterrows():
  101. # 跳过表头,但允许group为1的有效数据
  102. if index == 0 and str(row.iloc[0]).strip() in ['组别', 'group', '技能组']:
  103. continue
  104. # 检查是否为有效数据行
  105. if pd.isna(row.iloc[0]) or str(row.iloc[0]).strip() == '':
  106. continue
  107. skill_group = {
  108. "group": int(row.iloc[0]) if pd.notna(row.iloc[0]) else 0,
  109. "effectPercent": int(row.iloc[1]) if pd.notna(row.iloc[1]) else 0,
  110. "diamondCost": int(row.iloc[2]) if pd.notna(row.iloc[2]) else 0
  111. }
  112. skill_groups.append(skill_group)
  113. print(f"解析到 {len(skill_groups)} 个技能组")
  114. return skill_groups
  115. except Exception as e:
  116. print(f"解析技能组失败: {e}")
  117. return []
  118. def parse_basic_config(self, df):
  119. """解析基本配置"""
  120. basic_config = {}
  121. try:
  122. for index, row in df.iterrows():
  123. # 跳过表头
  124. if index == 0 or str(row.iloc[0]).strip() in ['配置项', 'config']:
  125. continue
  126. config_name = str(row.iloc[0]).strip()
  127. config_value = row.iloc[1]
  128. if config_name == '总组数':
  129. basic_config['totalGroups'] = int(config_value) if pd.notna(config_value) else 12
  130. elif config_name == '每组技能数':
  131. basic_config['skillsPerGroup'] = int(config_value) if pd.notna(config_value) else 3
  132. print(f"解析基本配置: {basic_config}")
  133. return basic_config
  134. except Exception as e:
  135. print(f"解析基本配置失败: {e}")
  136. return {}
  137. def parse_skill_config_data(self, excel_data):
  138. """解析技能配置数据"""
  139. try:
  140. config = self.default_config.copy()
  141. # 读取技能类型配置
  142. if "技能类型配置" in excel_data:
  143. skill_types = self.parse_skill_types(excel_data["技能类型配置"])
  144. config["skillTypes"] = skill_types
  145. elif "JSON技能类型配置" in excel_data:
  146. skill_types = self.parse_skill_types(excel_data["JSON技能类型配置"])
  147. config["skillTypes"] = skill_types
  148. # 读取技能组配置
  149. if "技能组配置" in excel_data:
  150. skill_groups = self.parse_skill_groups(excel_data["技能组配置"])
  151. config["skillGroups"] = skill_groups
  152. # 读取基本配置
  153. if "基本配置" in excel_data:
  154. basic_config = self.parse_basic_config(excel_data["基本配置"])
  155. config.update(basic_config)
  156. return config
  157. except Exception as e:
  158. print(f"解析技能配置数据失败: {e}")
  159. return self.default_config.copy()
  160. def validate_config(self, config):
  161. """验证配置数据"""
  162. try:
  163. # 检查必要字段
  164. required_fields = ['skillTypes', 'skillGroups', 'totalGroups', 'skillsPerGroup']
  165. for field in required_fields:
  166. if field not in config:
  167. print(f"警告: 缺少必要字段 {field}")
  168. return False
  169. # 检查技能类型
  170. if not isinstance(config['skillTypes'], list):
  171. print("错误: skillTypes 必须是列表")
  172. return False
  173. # 检查技能组
  174. if not isinstance(config['skillGroups'], list):
  175. print("错误: skillGroups 必须是列表")
  176. return False
  177. print("配置验证通过")
  178. return True
  179. except Exception as e:
  180. print(f"配置验证失败: {e}")
  181. return False
  182. def merge_skill_configs(self, existing_config, excel_config):
  183. """合并现有配置和Excel配置"""
  184. try:
  185. # 创建合并后的配置,保持原有结构
  186. merged_config = existing_config.copy()
  187. # 确保保持原有的skills数组(如果存在)
  188. if 'skills' not in merged_config:
  189. merged_config['skills'] = []
  190. # 更新技能类型,保持原有格式
  191. if 'skillTypes' in excel_config and excel_config['skillTypes']:
  192. merged_config['skillTypes'] = excel_config['skillTypes']
  193. print(f"更新技能类型配置,共 {len(excel_config['skillTypes'])} 个类型")
  194. # 更新技能组,保持原有格式
  195. if 'skillGroups' in excel_config and excel_config['skillGroups']:
  196. merged_config['skillGroups'] = excel_config['skillGroups']
  197. print(f"更新技能组配置,共 {len(excel_config['skillGroups'])} 个组")
  198. # 更新基本配置,保持原有字段
  199. if 'totalGroups' in excel_config:
  200. merged_config['totalGroups'] = excel_config['totalGroups']
  201. print(f"更新总组数: {excel_config['totalGroups']}")
  202. if 'skillsPerGroup' in excel_config:
  203. merged_config['skillsPerGroup'] = excel_config['skillsPerGroup']
  204. print(f"更新每组技能数: {excel_config['skillsPerGroup']}")
  205. print("配置合并完成")
  206. return merged_config
  207. except Exception as e:
  208. print(f"合并配置失败: {e}")
  209. return existing_config
  210. def backup_json_config(self):
  211. """备份现有JSON配置"""
  212. try:
  213. if self.json_file.exists():
  214. timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
  215. backup_file = self.json_file.parent / f"{self.json_file.stem}_backup_{timestamp}.json"
  216. with open(self.json_file, 'r', encoding='utf-8') as src:
  217. with open(backup_file, 'w', encoding='utf-8') as dst:
  218. dst.write(src.read())
  219. print(f"配置已备份到: {backup_file}")
  220. return backup_file
  221. else:
  222. print("JSON文件不存在,无需备份")
  223. return None
  224. except Exception as e:
  225. print(f"备份配置失败: {e}")
  226. return None
  227. def save_json_config(self, config):
  228. """保存配置到JSON文件"""
  229. try:
  230. # 确保目录存在
  231. self.json_file.parent.mkdir(parents=True, exist_ok=True)
  232. with open(self.json_file, 'w', encoding='utf-8') as f:
  233. json.dump(config, f, ensure_ascii=False, indent=2)
  234. print(f"配置已保存到: {self.json_file}")
  235. return True
  236. except Exception as e:
  237. print(f"保存JSON文件失败: {e}")
  238. return False
  239. def import_skill_config(self):
  240. """导入技能配置的主方法"""
  241. try:
  242. print("开始导入技能配置...")
  243. # 1. 加载现有JSON配置
  244. existing_config = self.load_existing_json_config()
  245. # 2. 读取Excel配置
  246. excel_data = self.read_excel_config()
  247. # 3. 解析Excel数据
  248. excel_config = self.parse_skill_config_data(excel_data)
  249. # 4. 验证配置
  250. if not self.validate_config(excel_config):
  251. print("配置验证失败,使用默认配置")
  252. excel_config = self.default_config.copy()
  253. # 5. 合并配置
  254. merged_config = self.merge_skill_configs(existing_config, excel_config)
  255. # 6. 备份现有配置
  256. self.backup_json_config()
  257. # 7. 保存新配置
  258. if self.save_json_config(merged_config):
  259. print("技能配置导入成功!")
  260. return True, "技能配置导入成功"
  261. else:
  262. print("技能配置保存失败!")
  263. return False, "技能配置保存失败"
  264. except Exception as e:
  265. error_msg = f"导入技能配置失败: {e}"
  266. print(error_msg)
  267. return False, error_msg
  268. def main():
  269. """主函数"""
  270. print("技能配置管理器")
  271. print("=" * 50)
  272. # 创建技能配置管理器
  273. manager = SkillConfigManager()
  274. # 导入配置
  275. success, message = manager.import_skill_config()
  276. if success:
  277. print(f"\n✓ {message}")
  278. else:
  279. print(f"\n✗ {message}")
  280. if __name__ == "__main__":
  281. main()