"""给指定用户(默认 11111111111)造「后台可驱动」的推送联调数据。 覆盖能从**管理后台点一下就触发手机推送**的 3 类事件,每类 10 条待审记录: 事件(PRD #) 后台动作 造的数据 ──────────────────────────────────────────────────────────────────── #10 反馈奖励 反馈工单 → 采纳(填回复留言 + 金币) 10 条 pending feedback(标「请采纳」) #9 官方回复 反馈工单 → 拒绝(填未采纳原因/留言) 10 条 pending feedback(标「请拒绝」) #11 爆料审核通过 上报更低价 → 通过 10 条 pending price_report 触发链路:admin 审核 → 发金币/改状态 → services/notification_events 落站内消息 + 厂商直推 → 该用户已注册设备(device_liveness)收到 push。 其余 3 类(#3 提现成功 / #4 提现失败 / #12 好友下单到账)后台无法在本环境驱动 (wxpay 未配 / 提现单唯一约束 / 后台无入口),用 scripts/fire_push_events.py 直接触发。 幂等:每次先删掉本脚本上一轮造的记录(按内容标记 [PUSH测试] 识别,不动用户真实反馈/爆料),再重建。 .venv\\Scripts\\python.exe scripts\\seed_push_admin_test.py .venv\\Scripts\\python.exe scripts\\seed_push_admin_test.py --phone 11111111111 --count 10 .venv\\Scripts\\python.exe scripts\\seed_push_admin_test.py --clean-only """ from __future__ import annotations import argparse import logging import sys from sqlalchemy import delete, select from app.db.session import SessionLocal from app.models.feedback import Feedback from app.models.price_report import PriceReport from app.repositories import user as user_repo logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING) # 静音 SQL 回显,输出更干净 if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8") DEFAULT_PHONE = "11111111111" MARK = "[PUSH测试]" # 本脚本造的数据统一带此标记,幂等清理按它识别(不误删真实数据) def clean(db, uid: int) -> tuple[int, int]: """删掉本脚本上一轮造的带标记记录(任何状态都删,彻底重置)。返回 (删反馈数, 删爆料数)。""" fb_ids = list(db.execute( select(Feedback.id).where(Feedback.user_id == uid, Feedback.content.like(f"{MARK}%")) ).scalars()) rep_ids = list(db.execute( select(PriceReport.id).where( PriceReport.user_id == uid, PriceReport.store_name.like(f"{MARK}%") ) ).scalars()) if fb_ids: db.execute(delete(Feedback).where(Feedback.id.in_(fb_ids))) if rep_ids: db.execute(delete(PriceReport).where(PriceReport.id.in_(rep_ids))) db.commit() return len(fb_ids), len(rep_ids) def seed(db, uid: int, count: int) -> None: # #10 反馈奖励:采纳这些 → 手机收「反馈奖励已到账」。采纳时记得在后台填「给用户的回复留言」 # (PRD 要求发奖必带官方留言),否则通知里不带留言行。 for i in range(1, count + 1): db.add(Feedback( user_id=uid, content=f"{MARK} 请【采纳】我 → 触发 #10 反馈奖励推送。测试反馈内容 {i:02d}:比价页能加个历史记录就好了。", contact="", source="profile", status="pending", )) # #9 官方回复:拒绝这些 → 手机收「您的反馈有回复啦」。拒绝时填「未采纳原因」+「回复留言」。 for i in range(1, count + 1): db.add(Feedback( user_id=uid, content=f"{MARK} 请【拒绝】我 → 触发 #9 官方回复推送。测试反馈内容 {i:02d}:希望支持某某小众平台比价。", contact="", source="comparison", scene="other", status="pending", )) # #11 爆料审核通过:通过这些 → 手机收「爆料审核通过」(发固定金币)。 for i in range(1, count + 1): db.add(PriceReport( user_id=uid, comparison_record_id=None, store_name=f"{MARK}测试火锅店{i:02d}", dish_summary="招牌套餐 × 1", original_platform_id="meituan-waimai", original_platform_name="美团外卖", original_price_cents=9900, reported_platform_id="jd-waimai", reported_platform_name="京东外卖", reported_price_cents=8800, images=[], status="pending", )) db.commit() def main() -> None: parser = argparse.ArgumentParser(description="造后台可驱动的推送联调数据(反馈×2 + 爆料)") parser.add_argument("--phone", default=DEFAULT_PHONE, help=f"目标用户手机号(默认 {DEFAULT_PHONE})") parser.add_argument("--count", type=int, default=10, help="每类造多少条(默认 10)") parser.add_argument("--clean-only", action="store_true", help="只清理本脚本造的数据,不重建") args = parser.parse_args() db = SessionLocal() try: user = user_repo.get_user_by_phone(db, args.phone) if user is None: print(f"❌ 用户 {args.phone} 不存在。请先用该手机号在 App 登录一次再跑本脚本。") return uid = user.id nf, nr = clean(db, uid) print(f"🧹 已清理上一轮 [PUSH测试] 数据:反馈 {nf} 条、爆料 {nr} 条") if args.clean_only: print("✅ 仅清理,已完成。") return seed(db, uid, args.count) print(f"\n✅ 已为 {args.phone}(id={uid})造好后台联调数据(每类 {args.count} 条):") print(f" • 反馈工单「请采纳」× {args.count} → 后台【采纳】(填回复留言+金币)→ 手机收 #10 反馈奖励") print(f" • 反馈工单「请拒绝」× {args.count} → 后台【拒绝】(填未采纳原因/留言)→ 手机收 #9 官方回复") print(f" • 上报更低价 × {args.count} → 后台【通过】→ 手机收 #11 爆料审核通过") print("\n👉 打开管理后台(:8771)对应列表即可看到这些待审记录,逐条审核就会推到手机。") print(" #3/#4/#12 本环境后台驱动不了,用:.venv\\Scripts\\python.exe scripts\\fire_push_events.py") finally: db.close() if __name__ == "__main__": main()