test_e2e_full_flow.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. """
  2. 完整端到端测试:
  3. 1. 生成模拟决策数据
  4. 2. 发送飞书审批(个人 + 项目群)
  5. 3. 验证权限设置(anyone_editable)
  6. 4. 检查表格格式和表头
  7. """
  8. import asyncio
  9. import sys
  10. from pathlib import Path
  11. from datetime import datetime
  12. import pandas as pd
  13. sys.path.insert(0, str(Path(__file__).parent.parent.parent))
  14. from dotenv import load_dotenv
  15. load_dotenv()
  16. from agent.tools.models import ToolContext
  17. from config import FEISHU_OPERATOR_OPEN_ID, FEISHU_AD_PROJECT_CHAT_ID
  18. # 导入审批工具
  19. from tools.im_approval import send_approval_request
  20. async def main():
  21. print("=" * 70)
  22. print(" 完整端到端测试:审批流程 + 飞书在线表格")
  23. print("=" * 70)
  24. print()
  25. print("配置验证:")
  26. print(f" 个人 Open ID: {FEISHU_OPERATOR_OPEN_ID}")
  27. print(f" 项目群聊 ID: {FEISHU_AD_PROJECT_CHAT_ID}")
  28. print()
  29. if not FEISHU_OPERATOR_OPEN_ID or not FEISHU_AD_PROJECT_CHAT_ID:
  30. print("❌ 错误:配置缺失")
  31. return
  32. # 步骤1:生成模拟决策数据(验证后的决策CSV)
  33. print("步骤 1/4:生成模拟决策数据")
  34. print("-" * 70)
  35. # 创建模拟数据(包含所有审批表头字段)
  36. mock_data = [
  37. {
  38. "ad_id": 12345678,
  39. "account_id": 80769799,
  40. "ad_name": "R500_测试广告_1_小程序引流",
  41. "audience_tier": "R500",
  42. "ad_age_days": 15,
  43. "bid_amount": 15000, # 150元(单位:分)
  44. "cost_7d_avg": 850.5,
  45. "cost_7d_total": 5953.5,
  46. "revenue_7d_total": 8930.25,
  47. "动态ROI_7日均值": 1.50,
  48. "final_action": "pause",
  49. "action": "pause",
  50. "dimension": "ROI低于同类",
  51. "reason": "动态ROI为1.5,低于R500组中位数2.8的46%;消耗稳定但ROI持续偏低,建议关停",
  52. "recommended_change_pct": 0.0,
  53. "configured_status": "AD_STATUS_NORMAL",
  54. "tier": 2, # Tier 2需要审批
  55. },
  56. {
  57. "ad_id": 12345679,
  58. "account_id": 80769799,
  59. "ad_name": "R500_测试广告_2_小程序引流",
  60. "audience_tier": "R500",
  61. "ad_age_days": 20,
  62. "bid_amount": 18000,
  63. "cost_7d_avg": 1200.8,
  64. "cost_7d_total": 8405.6,
  65. "revenue_7d_total": 20170.44,
  66. "动态ROI_7日均值": 2.40,
  67. "final_action": "bid_down",
  68. "action": "bid_down",
  69. "dimension": "ROI低于同类",
  70. "reason": "动态ROI为2.4,低于R500组中位数2.8的14%;裂变率0.52低于同类均值0.62的16%;7日均消耗1200元,建议降价3%",
  71. "recommended_change_pct": 0.03,
  72. "configured_status": "AD_STATUS_NORMAL",
  73. "tier": 2,
  74. },
  75. {
  76. "ad_id": 12345680,
  77. "account_id": 80769799,
  78. "ad_name": "R330_测试广告_1_小程序引流",
  79. "audience_tier": "R330",
  80. "ad_age_days": 12,
  81. "bid_amount": 12000,
  82. "cost_7d_avg": 680.2,
  83. "cost_7d_total": 4761.4,
  84. "revenue_7d_total": 8094.38,
  85. "动态ROI_7日均值": 1.70,
  86. "final_action": "bid_down",
  87. "action": "bid_down",
  88. "dimension": "ROI低于同类",
  89. "reason": "动态ROI为1.7,低于R330组中位数2.0的15%;裂变率0.48低于同类均值0.55的13%;7日均消耗680元,建议降价5%",
  90. "recommended_change_pct": 0.05,
  91. "configured_status": "AD_STATUS_NORMAL",
  92. "tier": 3, # Tier 3需要审批
  93. },
  94. ]
  95. df = pd.DataFrame(mock_data)
  96. # 保存为验证后的CSV
  97. outputs_dir = Path("outputs/reports")
  98. outputs_dir.mkdir(parents=True, exist_ok=True)
  99. validated_csv = outputs_dir / "validated_decisions_test_e2e.csv"
  100. df.to_csv(validated_csv, index=False, encoding="utf-8-sig")
  101. print(f"✅ 生成 {len(df)} 条模拟决策数据")
  102. print(f" 文件: {validated_csv}")
  103. print(f" 内容: {len(df[df['tier'] >= 2])} 条需审批(Tier 2/3)")
  104. print()
  105. # 步骤2:发送飞书审批
  106. print("步骤 2/4:发送飞书审批")
  107. print("-" * 70)
  108. ctx = ToolContext()
  109. try:
  110. result = await send_approval_request(
  111. ctx=ctx,
  112. validated_csv=str(validated_csv),
  113. wait_for_reply=False, # 非阻塞模式,仅发送不等待回复
  114. timeout_minutes=5,
  115. )
  116. print("✅ 审批请求发送成功!")
  117. print()
  118. print(f"标题: {result.title}")
  119. print(f"输出: {result.output}")
  120. print()
  121. if result.metadata:
  122. request_id = result.metadata.get("request_id")
  123. print(f"请求ID: {request_id}")
  124. print(f"发送状态: {result.metadata.get('feishu_sent', False)}")
  125. print()
  126. except Exception as e:
  127. print(f"❌ 发送失败: {e}")
  128. import traceback
  129. traceback.print_exc()
  130. return
  131. # 步骤3:验证结果
  132. print("步骤 3/4:验证检查项")
  133. print("-" * 70)
  134. print()
  135. print("请手动验证以下项目:")
  136. print()
  137. print("✅ 1. 个人飞书消息")
  138. print(f" 检查 {FEISHU_OPERATOR_OPEN_ID} 是否收到:")
  139. print(" - 审批文本消息(包含'请回复通过或拒绝')")
  140. print(" - 在线表格链接(或文件附件)")
  141. print()
  142. print("✅ 2. 项目群聊消息")
  143. print(f" 检查群聊 {FEISHU_AD_PROJECT_CHAT_ID} 是否收到:")
  144. print(" - 相同的审批文本消息")
  145. print(" - 相同的在线表格链接(或文件附件)")
  146. print()
  147. print("✅ 3. 在线表格权限")
  148. print(" 打开表格链接,检查:")
  149. print(" - 是否能直接打开(无需权限申请)")
  150. print(" - 是否有\"编辑\"按钮(anyone_editable)")
  151. print(" - 尝试修改单元格内容,确认可编辑")
  152. print()
  153. print("✅ 4. 表格表头和格式")
  154. print(" 检查表格是否包含以下列:")
  155. for i, col in enumerate([
  156. "approval_date", "account_id", "ad_id", "cost_7d_avg", "action",
  157. "ad_name", "audience_tier", "ad_age_days", "bid_amount",
  158. "动态ROI_7日均值", "cost_7d_total", "revenue_7d_total",
  159. "dimension", "reason", "recommended_change_pct"
  160. ], 1):
  161. print(f" {i:2d}. {col}")
  162. print()
  163. print("✅ 5. E1黄色高亮")
  164. print(" 检查单元格 E1(action列标题)是否有黄色背景")
  165. print()
  166. # 步骤4:回复测试提示
  167. print("步骤 4/4:回复测试")
  168. print("-" * 70)
  169. print()
  170. print("现在可以测试回复功能:")
  171. print(" 1. 在个人私聊中回复 '通过'")
  172. print(" 或")
  173. print(" 2. 在项目群聊中回复 '通过'")
  174. print()
  175. print("系统应该能识别任一处的回复。")
  176. print()
  177. print("=" * 70)
  178. print(" 测试完成")
  179. print("=" * 70)
  180. print()
  181. print("📋 总结:")
  182. print(" ✅ 模拟数据生成完成")
  183. print(" ✅ 飞书审批发送完成")
  184. print(" ⏳ 等待手动验证")
  185. print()
  186. if __name__ == "__main__":
  187. asyncio.run(main())