""" 完整端到端测试: 1. 生成模拟决策数据 2. 发送飞书审批(个人 + 项目群) 3. 验证权限设置(anyone_editable) 4. 检查表格格式和表头 """ import asyncio import sys from pathlib import Path from datetime import datetime import pandas as pd sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from dotenv import load_dotenv load_dotenv() from agent.tools.models import ToolContext from config import FEISHU_OPERATOR_OPEN_ID, FEISHU_AD_PROJECT_CHAT_ID # 导入审批工具 from tools.im_approval import send_approval_request async def main(): print("=" * 70) print(" 完整端到端测试:审批流程 + 飞书在线表格") print("=" * 70) print() print("配置验证:") print(f" 个人 Open ID: {FEISHU_OPERATOR_OPEN_ID}") print(f" 项目群聊 ID: {FEISHU_AD_PROJECT_CHAT_ID}") print() if not FEISHU_OPERATOR_OPEN_ID or not FEISHU_AD_PROJECT_CHAT_ID: print("❌ 错误:配置缺失") return # 步骤1:生成模拟决策数据(验证后的决策CSV) print("步骤 1/4:生成模拟决策数据") print("-" * 70) # 创建模拟数据(包含所有审批表头字段) mock_data = [ { "ad_id": 12345678, "account_id": 80769799, "ad_name": "R500_测试广告_1_小程序引流", "audience_tier": "R500", "ad_age_days": 15, "bid_amount": 15000, # 150元(单位:分) "cost_7d_avg": 850.5, "cost_7d_total": 5953.5, "revenue_7d_total": 8930.25, "动态ROI_7日均值": 1.50, "final_action": "pause", "action": "pause", "dimension": "ROI低于同类", "reason": "动态ROI为1.5,低于R500组中位数2.8的46%;消耗稳定但ROI持续偏低,建议关停", "recommended_change_pct": 0.0, "configured_status": "AD_STATUS_NORMAL", "tier": 2, # Tier 2需要审批 }, { "ad_id": 12345679, "account_id": 80769799, "ad_name": "R500_测试广告_2_小程序引流", "audience_tier": "R500", "ad_age_days": 20, "bid_amount": 18000, "cost_7d_avg": 1200.8, "cost_7d_total": 8405.6, "revenue_7d_total": 20170.44, "动态ROI_7日均值": 2.40, "final_action": "bid_down", "action": "bid_down", "dimension": "ROI低于同类", "reason": "动态ROI为2.4,低于R500组中位数2.8的14%;裂变率0.52低于同类均值0.62的16%;7日均消耗1200元,建议降价3%", "recommended_change_pct": 0.03, "configured_status": "AD_STATUS_NORMAL", "tier": 2, }, { "ad_id": 12345680, "account_id": 80769799, "ad_name": "R330_测试广告_1_小程序引流", "audience_tier": "R330", "ad_age_days": 12, "bid_amount": 12000, "cost_7d_avg": 680.2, "cost_7d_total": 4761.4, "revenue_7d_total": 8094.38, "动态ROI_7日均值": 1.70, "final_action": "bid_down", "action": "bid_down", "dimension": "ROI低于同类", "reason": "动态ROI为1.7,低于R330组中位数2.0的15%;裂变率0.48低于同类均值0.55的13%;7日均消耗680元,建议降价5%", "recommended_change_pct": 0.05, "configured_status": "AD_STATUS_NORMAL", "tier": 3, # Tier 3需要审批 }, ] df = pd.DataFrame(mock_data) # 保存为验证后的CSV outputs_dir = Path("outputs/reports") outputs_dir.mkdir(parents=True, exist_ok=True) validated_csv = outputs_dir / "validated_decisions_test_e2e.csv" df.to_csv(validated_csv, index=False, encoding="utf-8-sig") print(f"✅ 生成 {len(df)} 条模拟决策数据") print(f" 文件: {validated_csv}") print(f" 内容: {len(df[df['tier'] >= 2])} 条需审批(Tier 2/3)") print() # 步骤2:发送飞书审批 print("步骤 2/4:发送飞书审批") print("-" * 70) ctx = ToolContext() try: result = await send_approval_request( ctx=ctx, validated_csv=str(validated_csv), wait_for_reply=False, # 非阻塞模式,仅发送不等待回复 timeout_minutes=5, ) print("✅ 审批请求发送成功!") print() print(f"标题: {result.title}") print(f"输出: {result.output}") print() if result.metadata: request_id = result.metadata.get("request_id") print(f"请求ID: {request_id}") print(f"发送状态: {result.metadata.get('feishu_sent', False)}") print() except Exception as e: print(f"❌ 发送失败: {e}") import traceback traceback.print_exc() return # 步骤3:验证结果 print("步骤 3/4:验证检查项") print("-" * 70) print() print("请手动验证以下项目:") print() print("✅ 1. 个人飞书消息") print(f" 检查 {FEISHU_OPERATOR_OPEN_ID} 是否收到:") print(" - 审批文本消息(包含'请回复通过或拒绝')") print(" - 在线表格链接(或文件附件)") print() print("✅ 2. 项目群聊消息") print(f" 检查群聊 {FEISHU_AD_PROJECT_CHAT_ID} 是否收到:") print(" - 相同的审批文本消息") print(" - 相同的在线表格链接(或文件附件)") print() print("✅ 3. 在线表格权限") print(" 打开表格链接,检查:") print(" - 是否能直接打开(无需权限申请)") print(" - 是否有\"编辑\"按钮(anyone_editable)") print(" - 尝试修改单元格内容,确认可编辑") print() print("✅ 4. 表格表头和格式") print(" 检查表格是否包含以下列:") for i, col in enumerate([ "approval_date", "account_id", "ad_id", "cost_7d_avg", "action", "ad_name", "audience_tier", "ad_age_days", "bid_amount", "动态ROI_7日均值", "cost_7d_total", "revenue_7d_total", "dimension", "reason", "recommended_change_pct" ], 1): print(f" {i:2d}. {col}") print() print("✅ 5. E1黄色高亮") print(" 检查单元格 E1(action列标题)是否有黄色背景") print() # 步骤4:回复测试提示 print("步骤 4/4:回复测试") print("-" * 70) print() print("现在可以测试回复功能:") print(" 1. 在个人私聊中回复 '通过'") print(" 或") print(" 2. 在项目群聊中回复 '通过'") print() print("系统应该能识别任一处的回复。") print() print("=" * 70) print(" 测试完成") print("=" * 70) print() print("📋 总结:") print(" ✅ 模拟数据生成完成") print(" ✅ 飞书审批发送完成") print(" ⏳ 等待手动验证") print() if __name__ == "__main__": asyncio.run(main())