Files
aiagent/backend/app/core/sms_service.py
renjianbo 876789fac1 feat: multi-tenant workspace isolation, RBAC, sidebar nav, billing, and Android enhancements
- Backend: workspace_id isolation for 14 model tables + safe migration/backfill
- Backend: RBAC system with 4 roles and 23 permissions, seeded on startup
- Backend: workspace admin endpoints (list/manage all workspaces)
- Backend: admin user management API (CRUD, reset password)
- Backend: billing API with subscription plans, usage tracking, rate limiting
- Backend: fix system_logs.py UNION query and wrong column references
- Backend: WebSocket JWT auth and workspace enforcement
- Frontend: sidebar navigation replacing top dropdown menu
- Frontend: user management page (Users.vue) for admins
- Frontend: enhanced Workspaces.vue with admin table view
- Frontend: workspace RBAC computed properties in user store
- Android: agent marketplace, billing/subscription UI, onboarding wizard
- Android: phone login, analytics tracker, crash handler, network diagnostics
- Android: splash screen, encrypted token storage, app update enhancements
- Docs: multi-tenant RBAC guide with 8 sections and role-permission matrix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-04 01:00:22 +08:00

84 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
SMS 服务抽象层
Mock 模式:验证码打印到后端日志
后续可接入阿里云短信 / 腾讯云 SMS只需替换 Provider 实现
"""
import logging
from abc import ABC, abstractmethod
from app.core.config import settings
logger = logging.getLogger(__name__)
class SmsProvider(ABC):
"""SMS 发送提供者基类"""
@abstractmethod
async def send(self, phone: str, code: str) -> bool:
"""发送验证码短信,成功返回 True"""
...
class MockSmsProvider(SmsProvider):
"""Mock 提供者 —— 验证码打印到后端日志(开发/测试环境用)"""
async def send(self, phone: str, code: str) -> bool:
logger.info("=" * 50)
logger.info("📱 [MockSMS] 验证码: %s%s", code, phone)
logger.info("=" * 50)
return True
class TencentSmsProvider(SmsProvider):
"""腾讯云 SMS 提供者(待接入)"""
def __init__(self, secret_id: str, secret_key: str, app_id: str, sign_name: str, template_id: str):
self.secret_id = secret_id
self.secret_key = secret_key
self.app_id = app_id
self.sign_name = sign_name
self.template_id = template_id
async def send(self, phone: str, code: str) -> bool:
# TODO: 接入腾讯云 SMS SDK
logger.warning("TencentSmsProvider 尚未实现,验证码: %s%s", code, phone)
return False
class AliyunSmsProvider(SmsProvider):
"""阿里云短信提供者(待接入)"""
def __init__(self, access_key_id: str, access_key_secret: str, sign_name: str, template_code: str):
self.access_key_id = access_key_id
self.access_key_secret = access_key_secret
self.sign_name = sign_name
self.template_code = template_code
async def send(self, phone: str, code: str) -> bool:
# TODO: 接入阿里云短信 SDK
logger.warning("AliyunSmsProvider 尚未实现,验证码: %s%s", code, phone)
return False
def get_sms_provider() -> SmsProvider:
"""根据配置返回 SMS 提供者实例"""
provider_name = getattr(settings, "SMS_PROVIDER", "mock") or "mock"
if provider_name == "tencent":
return TencentSmsProvider(
secret_id=getattr(settings, "TENCENT_SMS_SECRET_ID", ""),
secret_key=getattr(settings, "TENCENT_SMS_SECRET_KEY", ""),
app_id=getattr(settings, "TENCENT_SMS_APP_ID", ""),
sign_name=getattr(settings, "TENCENT_SMS_SIGN_NAME", ""),
template_id=getattr(settings, "TENCENT_SMS_TEMPLATE_ID", ""),
)
elif provider_name == "aliyun":
return AliyunSmsProvider(
access_key_id=getattr(settings, "ALIYUN_SMS_ACCESS_KEY_ID", ""),
access_key_secret=getattr(settings, "ALIYUN_SMS_ACCESS_KEY_SECRET", ""),
sign_name=getattr(settings, "ALIYUN_SMS_SIGN_NAME", ""),
template_code=getattr(settings, "ALIYUN_SMS_TEMPLATE_CODE", ""),
)
else:
return MockSmsProvider()