dimension_analysis.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. """
  2. 维度分析模块 - V1 简化版
  3. 提供维度数据的收集、分析、建议生成和用户交互功能
  4. """
  5. import json
  6. import os
  7. import sys
  8. from pathlib import Path
  9. from datetime import datetime, timedelta
  10. from typing import List, Dict, Optional, Any
  11. from collections import defaultdict
  12. # 设置控制台编码为UTF-8(Windows)
  13. # 注意:只在作为主脚本运行时重定向,避免在被导入时冲突
  14. if sys.platform == 'win32' and __name__ == "__main__":
  15. import io
  16. if not isinstance(sys.stdout, io.TextIOWrapper):
  17. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
  18. if not isinstance(sys.stderr, io.TextIOWrapper):
  19. sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
  20. # ==================== 数据收集功能 ====================
  21. def collect_daily_records(archive_dir: Path) -> List[Dict]:
  22. """从 archive/youtube/ 目录读取所有日报 JSON"""
  23. records = []
  24. if not archive_dir.exists():
  25. return records
  26. for json_file in archive_dir.glob("*.json"):
  27. # 跳过 research 报告
  28. if json_file.name.endswith("_research.json"):
  29. continue
  30. try:
  31. with open(json_file, 'r', encoding='utf-8') as f:
  32. data = json.load(f)
  33. # 确保有 dimensions 字段(向后兼容)
  34. if 'dimensions' not in data:
  35. data['dimensions'] = []
  36. records.append(data)
  37. except Exception as e:
  38. print(f"⚠️ 读取文件失败 {json_file.name}: {e}")
  39. return records
  40. def collect_weekly_records(weekly_dir: Path) -> List[Dict]:
  41. """从指定目录读取周报 JSON"""
  42. records = []
  43. if not weekly_dir.exists():
  44. return records
  45. for json_file in weekly_dir.glob("*.json"):
  46. try:
  47. with open(json_file, 'r', encoding='utf-8') as f:
  48. data = json.load(f)
  49. if 'dimensions' not in data:
  50. data['dimensions'] = []
  51. records.append(data)
  52. except Exception as e:
  53. print(f"⚠️ 读取周报文件失败 {json_file.name}: {e}")
  54. return records
  55. def collect_monthly_records(monthly_dir: Path) -> List[Dict]:
  56. """从指定目录读取月报 JSON"""
  57. records = []
  58. if not monthly_dir.exists():
  59. return records
  60. for json_file in monthly_dir.glob("*.json"):
  61. try:
  62. with open(json_file, 'r', encoding='utf-8') as f:
  63. data = json.load(f)
  64. if 'dimensions' not in data:
  65. data['dimensions'] = []
  66. records.append(data)
  67. except Exception as e:
  68. print(f"⚠️ 读取月报文件失败 {json_file.name}: {e}")
  69. return records
  70. def load_all_records(base_dir: Path) -> Dict[str, List[Dict]]:
  71. """统一加载所有类型的记录"""
  72. archive_dir = base_dir / "archive" / "youtube"
  73. weekly_dir = base_dir / "archive" / "weekly"
  74. monthly_dir = base_dir / "archive" / "monthly"
  75. return {
  76. "daily": collect_daily_records(archive_dir),
  77. "weekly": collect_weekly_records(weekly_dir),
  78. "monthly": collect_monthly_records(monthly_dir)
  79. }
  80. # ==================== 维度分析功能 ====================
  81. def parse_date(date_str: str) -> Optional[datetime]:
  82. """解析日期字符串为 datetime 对象"""
  83. try:
  84. # 支持多种日期格式
  85. for fmt in ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S"]:
  86. try:
  87. return datetime.strptime(date_str, fmt)
  88. except ValueError:
  89. continue
  90. return None
  91. except Exception:
  92. return None
  93. def count_dimension_frequency(records: List[Dict]) -> Dict[str, Dict]:
  94. """统计每个维度的出现频率"""
  95. dimension_stats = defaultdict(lambda: {
  96. 'frequency': 0,
  97. 'dates': [],
  98. 'first_seen': None,
  99. 'last_seen': None
  100. })
  101. total_records = len(records)
  102. for record in records:
  103. date_str = record.get('date', '')
  104. dimensions = record.get('dimensions', [])
  105. if not dimensions:
  106. continue
  107. record_date = parse_date(date_str)
  108. for dim in dimensions:
  109. if dim: # 跳过空字符串
  110. dimension_stats[dim]['frequency'] += 1
  111. if record_date:
  112. dimension_stats[dim]['dates'].append(record_date)
  113. if dimension_stats[dim]['first_seen'] is None or record_date < dimension_stats[dim]['first_seen']:
  114. dimension_stats[dim]['first_seen'] = record_date
  115. if dimension_stats[dim]['last_seen'] is None or record_date > dimension_stats[dim]['last_seen']:
  116. dimension_stats[dim]['last_seen'] = record_date
  117. # 计算频率率和格式化日期
  118. result = {}
  119. for dim, stats in dimension_stats.items():
  120. result[dim] = {
  121. 'frequency': stats['frequency'],
  122. 'frequency_rate': stats['frequency'] / total_records if total_records > 0 else 0.0,
  123. 'first_seen': stats['first_seen'].strftime("%Y-%m-%d") if stats['first_seen'] else None,
  124. 'last_seen': stats['last_seen'].strftime("%Y-%m-%d") if stats['last_seen'] else None,
  125. 'dates': [d.strftime("%Y-%m-%d") for d in stats['dates']]
  126. }
  127. return result
  128. def find_missing_dimensions(records: List[Dict], candidate_dimensions: List[str], days_threshold: int = 30) -> List[str]:
  129. """查找缺失的维度(超过N天未出现)"""
  130. now = datetime.now()
  131. missing = []
  132. for dim in candidate_dimensions:
  133. # 查找该维度最后一次出现的时间
  134. last_seen = None
  135. for record in records:
  136. dimensions = record.get('dimensions', [])
  137. if dim in dimensions:
  138. date_str = record.get('date', '')
  139. record_date = parse_date(date_str)
  140. if record_date and (last_seen is None or record_date > last_seen):
  141. last_seen = record_date
  142. # 如果从未出现或超过阈值天数,加入缺失列表
  143. if last_seen is None:
  144. missing.append(dim)
  145. else:
  146. days_diff = (now - last_seen).days
  147. if days_diff > days_threshold:
  148. missing.append(dim)
  149. return missing
  150. # ==================== 优先级计算功能 ====================
  151. def calculate_dimension_priority(records: List[Dict]) -> Dict[str, float]:
  152. """计算维度优先级分数(仅基于出现频率)"""
  153. stats = count_dimension_frequency(records)
  154. priorities = {}
  155. for dim, dim_stats in stats.items():
  156. priorities[dim] = dim_stats['frequency_rate']
  157. return priorities
  158. # ==================== 建议生成功能 ====================
  159. def suggest_add_dimensions(records: List[Dict], candidate_dimensions: List[str], threshold_days: int = 30) -> List[Dict]:
  160. """建议添加缺失但重要的维度"""
  161. missing = find_missing_dimensions(records, candidate_dimensions, threshold_days)
  162. suggestions = []
  163. for dim in missing:
  164. # 计算建议的优先级(如果该维度曾经出现过,使用历史频率;否则使用默认值 0.5)
  165. stats = count_dimension_frequency(records)
  166. suggested_priority = stats.get(dim, {}).get('frequency_rate', 0.5)
  167. suggestions.append({
  168. "suggestion_id": f"add_{dim}_{datetime.now().strftime('%Y%m%d')}",
  169. "type": "add",
  170. "dimension": dim,
  171. "reason": f"已有{threshold_days}天未在记录中出现,但候选维度列表中",
  172. "recommendation": "建议添加该维度",
  173. "suggested_priority": round(suggested_priority, 2)
  174. })
  175. return suggestions
  176. def suggest_remove_dimensions(records: List[Dict], active_dimensions: List[str], threshold_days: int = 60) -> List[Dict]:
  177. """建议删除长期未出现的维度"""
  178. stats = count_dimension_frequency(records)
  179. suggestions = []
  180. now = datetime.now()
  181. for dim in active_dimensions:
  182. dim_stat = stats.get(dim, {})
  183. last_seen_str = dim_stat.get('last_seen')
  184. if not last_seen_str:
  185. # 从未出现过
  186. suggestions.append({
  187. "suggestion_id": f"remove_{dim}_{datetime.now().strftime('%Y%m%d')}",
  188. "type": "remove",
  189. "dimension": dim,
  190. "reason": "从未在记录中出现",
  191. "recommendation": "建议删除该维度"
  192. })
  193. else:
  194. last_seen = parse_date(last_seen_str)
  195. if last_seen:
  196. days_diff = (now - last_seen).days
  197. if days_diff > threshold_days:
  198. suggestions.append({
  199. "suggestion_id": f"remove_{dim}_{datetime.now().strftime('%Y%m%d')}",
  200. "type": "remove",
  201. "dimension": dim,
  202. "reason": f"已有{days_diff}天未在记录中出现",
  203. "recommendation": "建议删除该维度"
  204. })
  205. return suggestions
  206. def suggest_priority_adjustment(records: List[Dict], dimension_config: Dict) -> List[Dict]:
  207. """建议调整频繁出现维度的优先级"""
  208. stats = count_dimension_frequency(records)
  209. priorities = calculate_dimension_priority(records)
  210. suggestions = []
  211. active_dimensions = dimension_config.get('active_dimensions', [])
  212. for dim_info in active_dimensions:
  213. dim_name = dim_info.get('name')
  214. current_priority = dim_info.get('priority', 0.0)
  215. dim_stat = stats.get(dim_name, {})
  216. frequency_rate = dim_stat.get('frequency_rate', 0.0)
  217. # 如果频率 > 70% 且当前优先级 < 频率,建议提升
  218. if frequency_rate > 0.7 and current_priority < frequency_rate:
  219. suggestions.append({
  220. "suggestion_id": f"priority_{dim_name}_{datetime.now().strftime('%Y%m%d')}",
  221. "type": "priority_adjustment",
  222. "dimension": dim_name,
  223. "reason": f"最近出现频率达{frequency_rate*100:.1f}%,但当前优先级仅为{current_priority:.2f}",
  224. "current_priority": current_priority,
  225. "suggested_priority": round(frequency_rate, 2),
  226. "recommendation": "建议提高该维度的优先级"
  227. })
  228. return suggestions
  229. def generate_all_suggestions(records: List[Dict], dimension_config: Dict) -> Dict[str, List[Dict]]:
  230. """生成所有建议的综合报告"""
  231. all_records = records
  232. # 获取当前配置
  233. active_dimensions = [d['name'] for d in dimension_config.get('active_dimensions', [])]
  234. candidate_dimensions = dimension_config.get('candidate_dimensions', [])
  235. # 生成各类建议
  236. add_suggestions = suggest_add_dimensions(all_records, candidate_dimensions, threshold_days=30)
  237. remove_suggestions = suggest_remove_dimensions(all_records, active_dimensions, threshold_days=60)
  238. priority_suggestions = suggest_priority_adjustment(all_records, dimension_config)
  239. return {
  240. "add": add_suggestions,
  241. "remove": remove_suggestions,
  242. "priority_adjustment": priority_suggestions
  243. }
  244. # ==================== 维度与Themes对比功能 ====================
  245. def count_dimension_frequency_from_extractions(extraction_results: List[Dict]) -> Dict[str, Dict]:
  246. """从提取结果中统计维度频率"""
  247. dimension_stats = defaultdict(lambda: {
  248. 'frequency': 0,
  249. 'dates': [],
  250. 'first_seen': None,
  251. 'last_seen': None
  252. })
  253. total_extractions = len(extraction_results)
  254. for result in extraction_results:
  255. dimensions = result.get('dimensions', [])
  256. extraction_date_str = result.get('extraction_date', result.get('report_date', ''))
  257. extraction_date = parse_date(extraction_date_str.split('T')[0]) # 只取日期部分
  258. for dim in dimensions:
  259. if dim:
  260. dimension_stats[dim]['frequency'] += 1
  261. if extraction_date:
  262. dimension_stats[dim]['dates'].append(extraction_date)
  263. if dimension_stats[dim]['first_seen'] is None or extraction_date < dimension_stats[dim]['first_seen']:
  264. dimension_stats[dim]['first_seen'] = extraction_date
  265. if dimension_stats[dim]['last_seen'] is None or extraction_date > dimension_stats[dim]['last_seen']:
  266. dimension_stats[dim]['last_seen'] = extraction_date
  267. # 计算频率率和格式化日期
  268. result = {}
  269. for dim, stats in dimension_stats.items():
  270. result[dim] = {
  271. 'frequency': stats['frequency'],
  272. 'frequency_rate': stats['frequency'] / total_extractions if total_extractions > 0 else 0.0,
  273. 'first_seen': stats['first_seen'].strftime("%Y-%m-%d") if stats['first_seen'] else None,
  274. 'last_seen': stats['last_seen'].strftime("%Y-%m-%d") if stats['last_seen'] else None,
  275. }
  276. return result
  277. def analyze_theme_dimension_match(themes: List[str], extraction_results: List[Dict], days_window: int = 30) -> Dict[str, Dict]:
  278. """分析themes与维度的匹配度"""
  279. now = datetime.now()
  280. # 统计维度频率
  281. dim_stats = count_dimension_frequency_from_extractions(extraction_results)
  282. # 过滤最近N天的提取结果
  283. recent_results = []
  284. for result in extraction_results:
  285. extraction_date_str = result.get('extraction_date', result.get('report_date', ''))
  286. extraction_date = parse_date(extraction_date_str.split('T')[0])
  287. if extraction_date:
  288. days_diff = (now - extraction_date).days
  289. if days_diff <= days_window:
  290. recent_results.append(result)
  291. # 统计最近N天内的维度
  292. recent_dim_stats = count_dimension_frequency_from_extractions(recent_results)
  293. theme_match = {}
  294. for theme in themes:
  295. # 计算theme在提取维度中的匹配情况
  296. match_count = 0
  297. total_count = len(recent_results)
  298. for result in recent_results:
  299. dimensions = result.get('dimensions', [])
  300. # 简单匹配:theme是否在维度列表中(可以考虑更复杂的相似度匹配)
  301. if theme in dimensions:
  302. match_count += 1
  303. match_rate = match_count / total_count if total_count > 0 else 0.0
  304. # 计算最近一次匹配的时间
  305. last_match_date = None
  306. for result in recent_results:
  307. dimensions = result.get('dimensions', [])
  308. if theme in dimensions:
  309. extraction_date_str = result.get('extraction_date', result.get('report_date', ''))
  310. extraction_date = parse_date(extraction_date_str.split('T')[0])
  311. if extraction_date:
  312. if last_match_date is None or extraction_date > last_match_date:
  313. last_match_date = extraction_date
  314. theme_match[theme] = {
  315. 'match_rate': match_rate,
  316. 'match_count': match_count,
  317. 'total_count': total_count,
  318. 'last_match_date': last_match_date.strftime("%Y-%m-%d") if last_match_date else None,
  319. 'days_without_match': (now - last_match_date).days if last_match_date else days_window
  320. }
  321. return theme_match
  322. def suggest_add_themes(dim_stats: Dict[str, Dict], themes: List[str], threshold_frequency: float = 0.5, min_recent_count: int = 3, days_window: int = 30) -> List[Dict]:
  323. """建议添加新themes(维度中出现但themes中没有的)"""
  324. suggestions = []
  325. now = datetime.now()
  326. for dim, stats in dim_stats.items():
  327. # 如果维度不在themes中
  328. if dim not in themes:
  329. frequency_rate = stats.get('frequency_rate', 0.0)
  330. last_seen_str = stats.get('last_seen')
  331. # 检查最近出现次数
  332. recent_count = 0
  333. if last_seen_str:
  334. last_seen = parse_date(last_seen_str)
  335. if last_seen:
  336. days_diff = (now - last_seen).days
  337. if days_diff <= days_window:
  338. # 估算最近出现次数(简化:假设频率一致)
  339. recent_count = int(frequency_rate * (days_window / 7)) # 粗略估算
  340. # 如果频率达到阈值且最近有出现
  341. if frequency_rate >= threshold_frequency and recent_count >= min_recent_count:
  342. suggestions.append({
  343. "suggestion_id": f"add_theme_{dim}_{datetime.now().strftime('%Y%m%d')}",
  344. "type": "add_theme",
  345. "theme": dim,
  346. "reason": f"从报告中提取的维度'{dim}'出现频率{frequency_rate*100:.1f}%,最近{days_window}天出现{recent_count}次",
  347. "source_dimensions": [dim],
  348. "frequency": frequency_rate,
  349. "recent_count": recent_count
  350. })
  351. return suggestions
  352. def suggest_remove_themes(theme_match: Dict[str, Dict], threshold_frequency: float = 0.1, min_days: int = 60) -> List[Dict]:
  353. """建议删除themes(长期与维度不匹配的)"""
  354. suggestions = []
  355. for theme, match_info in theme_match.items():
  356. match_rate = match_info.get('match_rate', 0.0)
  357. days_without_match = match_info.get('days_without_match', 0)
  358. # 如果匹配率低于阈值且持续时间超过阈值
  359. if match_rate < threshold_frequency and days_without_match >= min_days:
  360. suggestions.append({
  361. "suggestion_id": f"remove_theme_{theme}_{datetime.now().strftime('%Y%m%d')}",
  362. "type": "remove_theme",
  363. "theme": theme,
  364. "reason": f"过去{min_days}天内,'{theme}'在提取维度中的匹配率仅{match_rate*100:.1f}%,且已有{days_without_match}天未匹配",
  365. "match_rate": match_rate,
  366. "days_without_match": days_without_match
  367. })
  368. return suggestions
  369. def generate_theme_suggestions(extraction_results: List[Dict], themes: List[str]) -> Dict[str, List[Dict]]:
  370. """生成themes修正建议"""
  371. # 统计维度频率
  372. dim_stats = count_dimension_frequency_from_extractions(extraction_results)
  373. # 分析themes匹配度(使用30天窗口)
  374. theme_match = analyze_theme_dimension_match(themes, extraction_results, days_window=30)
  375. # 生成建议
  376. add_suggestions = suggest_add_themes(dim_stats, themes, threshold_frequency=0.5, min_recent_count=3, days_window=30)
  377. remove_suggestions = suggest_remove_themes(theme_match, threshold_frequency=0.1, min_days=60)
  378. return {
  379. "add": add_suggestions,
  380. "remove": remove_suggestions,
  381. "theme_match_analysis": theme_match
  382. }
  383. # ==================== 配置文件管理 ====================
  384. def load_dimension_config(config_file: Path) -> Dict:
  385. """加载维度配置文件"""
  386. if config_file.exists():
  387. try:
  388. with open(config_file, 'r', encoding='utf-8') as f:
  389. return json.load(f)
  390. except Exception as e:
  391. print(f"⚠️ 加载配置文件失败: {e}")
  392. # 返回默认配置
  393. return {
  394. "active_dimensions": [],
  395. "candidate_dimensions": [],
  396. "removed_dimensions": []
  397. }
  398. def save_dimension_config(config_file: Path, config: Dict):
  399. """保存维度配置文件"""
  400. config_file.parent.mkdir(parents=True, exist_ok=True)
  401. with open(config_file, 'w', encoding='utf-8') as f:
  402. json.dump(config, f, indent=2, ensure_ascii=False)
  403. def load_dimension_history(history_file: Path) -> List[Dict]:
  404. """加载维度历史记录"""
  405. if history_file.exists():
  406. try:
  407. with open(history_file, 'r', encoding='utf-8') as f:
  408. data = json.load(f)
  409. return data.get('history', [])
  410. except Exception as e:
  411. print(f"⚠️ 加载历史记录失败: {e}")
  412. return []
  413. def save_dimension_history(history_file: Path, history: List[Dict]):
  414. """保存维度历史记录"""
  415. history_file.parent.mkdir(parents=True, exist_ok=True)
  416. data = {"history": history}
  417. with open(history_file, 'w', encoding='utf-8') as f:
  418. json.dump(data, f, indent=2, ensure_ascii=False)
  419. def record_dimension_event(event_type: str, dimension: str, timestamp: str = None, metadata: Dict = None) -> Dict:
  420. """记录维度事件(ADD/REMOVE/PRIORITY_CHANGE)"""
  421. if timestamp is None:
  422. timestamp = datetime.now().strftime("%Y-%m-%d")
  423. event = {
  424. "date": timestamp,
  425. "event": event_type,
  426. "dimension": dimension
  427. }
  428. if metadata:
  429. event.update(metadata)
  430. return event
  431. # ==================== 用户交互功能 ====================
  432. def present_suggestions(suggestions: Dict[str, List[Dict]]) -> None:
  433. """展示系统建议给用户(简单文本)"""
  434. print("\n" + "=" * 70)
  435. print("📋 维度调整建议")
  436. print("=" * 70)
  437. all_count = sum(len(v) for v in suggestions.values())
  438. if all_count == 0:
  439. print("✅ 暂无建议")
  440. return
  441. # 展示新增建议
  442. if suggestions.get('add'):
  443. print("\n【新增维度建议】")
  444. for i, sug in enumerate(suggestions['add'], 1):
  445. print(f" {i}. {sug['dimension']}")
  446. print(f" 原因: {sug['reason']}")
  447. print(f" 建议优先级: {sug['suggested_priority']}")
  448. # 展示删除建议
  449. if suggestions.get('remove'):
  450. print("\n【删除维度建议】")
  451. for i, sug in enumerate(suggestions['remove'], 1):
  452. print(f" {i}. {sug['dimension']}")
  453. print(f" 原因: {sug['reason']}")
  454. # 展示优先级调整建议
  455. if suggestions.get('priority_adjustment'):
  456. print("\n【优先级调整建议】")
  457. for i, sug in enumerate(suggestions['priority_adjustment'], 1):
  458. print(f" {i}. {sug['dimension']}")
  459. print(f" 原因: {sug['reason']}")
  460. print(f" 当前优先级: {sug['current_priority']:.2f} → 建议: {sug['suggested_priority']:.2f}")
  461. print("\n" + "=" * 70)
  462. def get_user_confirmation(suggestion: Dict) -> str:
  463. """获取用户确认(接受/拒绝,简单输入)"""
  464. print(f"\n建议: {suggestion['recommendation']}")
  465. print(f"维度: {suggestion['dimension']}")
  466. print(f"原因: {suggestion['reason']}")
  467. while True:
  468. user_input = input("接受 (y) / 拒绝 (n): ").strip().lower()
  469. if user_input in ['y', 'yes', '是', '接受']:
  470. return 'accepted'
  471. elif user_input in ['n', 'no', '否', '拒绝']:
  472. return 'rejected'
  473. else:
  474. print("⚠️ 请输入 y 或 n")
  475. def format_history_text(history: List[Dict]) -> str:
  476. """格式化历史记录为简单文本"""
  477. if not history:
  478. return "暂无历史记录"
  479. lines = ["维度演化历史:"]
  480. lines.append("-" * 70)
  481. for event in history:
  482. date = event.get('date', '')
  483. event_type = event.get('event', '')
  484. dimension = event.get('dimension', '')
  485. if event_type == "ADD":
  486. info = f"新增维度"
  487. elif event_type == "REMOVE":
  488. info = f"删除维度"
  489. elif event_type == "PRIORITY_CHANGE":
  490. old_priority = event.get('old_priority', '')
  491. new_priority = event.get('new_priority', '')
  492. info = f"优先级调整: {old_priority} → {new_priority}"
  493. else:
  494. info = event_type
  495. lines.append(f"{date} | {event_type} | {dimension} | {info}")
  496. return "\n".join(lines)