visitor.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. from urllib.parse import urlparse
  2. from sqlalchemy import select
  3. from sqlalchemy.ext.asyncio import AsyncSession
  4. from app.models.visitor import VisitorInfo
  5. from app.models.domain import MonitoredDomain
  6. from app.schemas.visitor import VisitorInfoCreate
  7. from app.services.sms import send_sms_code, generate_verify_code
  8. import logging
  9. logger = logging.getLogger(__name__)
  10. def _extract_domain_from_referer(referer: str | None) -> str | None:
  11. """从 Referer 头提取域名"""
  12. if not referer:
  13. return None
  14. try:
  15. parsed = urlparse(referer)
  16. host = parsed.hostname
  17. if not host:
  18. host = referer.strip().rstrip("/")
  19. if not host:
  20. return None
  21. netloc = parsed.netloc if parsed.netloc else host
  22. return host, netloc
  23. except Exception:
  24. return None
  25. async def store_visitor_info(
  26. db: AsyncSession, referer: str | None, payload: VisitorInfoCreate
  27. ) -> dict:
  28. """通过 Referer 匹配域名,存储联系人信息。同一域名覆盖更新。"""
  29. result = _extract_domain_from_referer(referer)
  30. if not result:
  31. return {"success": False, "message": "缺少或无效的 Referer 头"}
  32. referer_host, referer_netloc = result
  33. # 匹配监控域名(支持多种存储格式)
  34. domain_result = await db.execute(
  35. select(MonitoredDomain).where(
  36. MonitoredDomain.is_active == True,
  37. MonitoredDomain.domain.in_([
  38. referer_netloc,
  39. referer_host,
  40. f"http://{referer_netloc}",
  41. f"https://{referer_netloc}",
  42. ]),
  43. )
  44. )
  45. domain = domain_result.scalars().first()
  46. if not domain:
  47. return {"success": False, "message": "未找到匹配的域名"}
  48. # Upsert 联系人信息
  49. existing = await db.execute(
  50. select(VisitorInfo).where(VisitorInfo.domain_id == domain.id)
  51. )
  52. visitor = existing.scalar_one_or_none()
  53. if visitor:
  54. visitor.name = payload.name
  55. visitor.phone = payload.phone
  56. visitor.email = payload.email
  57. else:
  58. visitor = VisitorInfo(
  59. domain_id=domain.id,
  60. name=payload.name,
  61. phone=payload.phone,
  62. email=payload.email,
  63. )
  64. db.add(visitor)
  65. await db.commit()
  66. # 发送验证码短信,并将验证码返回给请求者
  67. code = None
  68. if payload.phone:
  69. code = generate_verify_code()
  70. await send_sms_code(payload.phone, code)
  71. return {"success": True, "message": "联系人信息已保存", "domain": domain.domain, "code": code}