send_domestic_sms_code.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """
  2. 发送国内短信验证码脚本
  3. 扫描 verification_codes 表,查找未发送的国内短信验证码并发送
  4. 运行: python send_domestic_sms_code.py
  5. """
  6. import asyncio
  7. import sys
  8. import logging
  9. from datetime import datetime
  10. from typing import List, Optional
  11. import aiomysql
  12. from sqlalchemy import select
  13. from sqlalchemy.ext.asyncio import AsyncSession
  14. from config import settings
  15. from database import DatabaseManager
  16. from models_db import VerificationCode
  17. logger = logging.getLogger(__name__)
  18. async def send_domestic_sms(phone: str, code: str) -> bool:
  19. """
  20. 发送国内短信验证码
  21. Args:
  22. phone: 手机号
  23. code: 验证码
  24. Returns:
  25. 是否发送成功
  26. """
  27. # TODO: 实现国内短信发送 API 调用
  28. # 示例:
  29. # async with httpx.AsyncClient() as client:
  30. # response = await client.post(
  31. # "https://api.sms-provider.com/send",
  32. # json={
  33. # "phone": phone,
  34. # "code": code,
  35. # "template": "verification_code"
  36. # }
  37. # )
  38. # return response.status_code == 200
  39. print(f"📱 [TODO] 发送国内短信: phone={phone}, code={code}")
  40. return False
  41. async def process_domestic_sms_codes():
  42. """扫描并发送国内短信验证码"""
  43. db_manager = DatabaseManager()
  44. try:
  45. await db_manager.connect()
  46. print("✅ 数据库连接成功")
  47. async with db_manager.get_session() as session:
  48. # 查询未发送的国内短信验证码
  49. result = await session.execute(
  50. select(VerificationCode)
  51. .where(
  52. VerificationCode.send_type == "domestic_sms",
  53. VerificationCode.is_sent == False,
  54. VerificationCode.is_valid == True,
  55. VerificationCode.phone.isnot(None)
  56. )
  57. .order_by(VerificationCode.created_at.asc())
  58. .limit(100) # 每次处理100条
  59. )
  60. codes = result.scalars().all()
  61. if not codes:
  62. print("ℹ️ 没有待发送的国内短信验证码")
  63. return
  64. print(f"📋 找到 {len(codes)} 条待发送的国内短信验证码")
  65. success_count = 0
  66. fail_count = 0
  67. for code_record in codes:
  68. try:
  69. # 发送短信
  70. success = await send_domestic_sms(code_record.phone, code_record.code)
  71. if success:
  72. # 更新发送状态
  73. code_record.is_sent = True
  74. code_record.sent_at = datetime.now()
  75. code_record.metadata_json = code_record.metadata_json or {}
  76. code_record.metadata_json["send_status"] = "success"
  77. code_record.metadata_json["send_at"] = datetime.now().isoformat()
  78. success_count += 1
  79. print(f"✅ 发送成功: code_id={code_record.code_id}, phone={code_record.phone}")
  80. else:
  81. # 记录失败信息
  82. code_record.metadata_json = code_record.metadata_json or {}
  83. code_record.metadata_json["send_status"] = "failed"
  84. code_record.metadata_json["error"] = "API调用失败"
  85. fail_count += 1
  86. print(f"❌ 发送失败: code_id={code_record.code_id}, phone={code_record.phone}")
  87. await session.commit()
  88. except Exception as e:
  89. print(f"❌ 处理验证码失败: code_id={code_record.code_id}, error={e}")
  90. code_record.metadata_json = code_record.metadata_json or {}
  91. code_record.metadata_json["send_status"] = "error"
  92. code_record.metadata_json["error"] = str(e)
  93. await session.commit()
  94. fail_count += 1
  95. print(f"\n📊 处理完成: 成功 {success_count} 条, 失败 {fail_count} 条")
  96. except Exception as e:
  97. print(f"❌ 处理失败: {e}")
  98. import traceback
  99. traceback.print_exc()
  100. sys.exit(1)
  101. finally:
  102. await db_manager.close()
  103. if __name__ == "__main__":
  104. logging.basicConfig(
  105. level=logging.INFO,
  106. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  107. datefmt='%Y-%m-%d %H:%M:%S'
  108. )
  109. asyncio.run(process_domestic_sms_codes())