# SMS 服务规范符合性修正 > **日期**:2026-01-26 > **类型**:文档修正 + 架构优化 > **影响范围**:短信验证码服务文档 --- ## 变更概述 对 `sms-service.md` 文档进行全面修正,使其完全符合 Jointo 技术栈规范。 --- ## 主要变更 ### 1. 数据库设计修正 #### 主键类型 - ❌ 旧:`code_id BIGINT GENERATED ALWAYS AS IDENTITY` - ✅ 新:`id UUID PRIMARY KEY DEFAULT gen_uuid_v7()` #### 时间戳字段 - ❌ 旧:仅 `created_at` - ✅ 新:`created_at`, `updated_at`, `deleted_at`(支持软删除) #### 枚举类型处理 - ❌ 旧:`purpose TEXT CHECK (purpose IN ('login', 'bind_phone'))` - ✅ 新:`purpose SMALLINT` + Python `IntEnum` ```python class SmsPurpose(IntEnum): LOGIN = 1 BIND_PHONE = 2 RESET_PASSWORD = 3 ``` #### 索引优化 新增条件索引: ```sql CREATE INDEX idx_sms_codes_active ON sms_verification_codes(expires_at) WHERE verified = false AND deleted_at IS NULL; ``` --- ### 2. 异步编程规范 #### Redis 客户端 - ❌ 旧:`redis.from_url()` 同步客户端 - ✅ 新:`Redis.from_url()` 异步客户端(`redis.asyncio`) ```python from redis.asyncio import Redis self.redis_client = Redis.from_url( settings.REDIS_URL, encoding="utf-8", decode_responses=True ) ``` #### HTTP 客户端 - ❌ 旧:阿里云 SDK 同步调用 - ✅ 新:`httpx.AsyncClient` 异步调用 ```python self.http_client = httpx.AsyncClient(timeout=10.0) await self.http_client.post(url, params=params) ``` #### 限流检查 所有 Redis 操作改为异步: ```python # 旧 ip_count = self.redis_client.get(ip_key) # 新 ip_count = await self.redis_client.get(ip_key) ``` --- ### 3. API 响应格式标准化 #### 成功响应 - ❌ 旧:`{"message": "...", "expires_in": 600}` - ✅ 新:标准格式 ```json { "code": 200, "message": "Success", "data": { "message": "验证码已发送", "expiresIn": 600 } } ``` #### 错误响应 - ❌ 旧:`{"error": "RateLimitError", "message": "..."}` - ✅ 新:标准格式 ```json { "code": 429, "message": "发送过于频繁,请稍后再试", "data": null } ``` --- ### 4. 架构层次完善 新增完整的三层架构说明: #### Model 层(app/models/sms.py) - `SmsPurpose` IntEnum - `SmsVerificationCode` SQLModel #### Schema 层(app/schemas/sms.py) - `SmsPurposeEnum` 字符串枚举(API 层) - `SmsCodeSendRequest` 请求模型 - `SmsCodeVerifyRequest` 验证模型 #### Repository 层(app/repositories/sms_repository.py) - 数据访问层完整实现 - 异步查询方法 --- ### 5. 类型安全改进 #### Service 方法签名 ```python # 旧 async def send_verification_code( self, phone: str, purpose: str = 'login', # 字符串 ... ) -> None # 新 async def send_verification_code( self, phone: str, purpose: SmsPurpose = SmsPurpose.LOGIN, # 枚举 ... ) -> dict # 明确返回类型 ``` --- ## 技术债务清理 ### 移除的依赖 - ❌ `aliyunsdkcore`(同步 SDK) - ❌ `aliyunsdkdysmsapi`(同步 SDK) ### 新增的依赖 - ✅ `redis[asyncio]>=5.0.1` - ✅ `httpx>=0.27.0` --- ## 迁移指南 ### 1. 更新依赖 ```bash pip install redis[asyncio]>=5.0.1 httpx>=0.27.0 pip uninstall aliyunsdkcore aliyunsdkdysmsapi ``` ### 2. 数据库迁移 需要创建新的迁移脚本: ```python # migrations/00X_sms_verification_codes.py async def upgrade(session: AsyncSession): # 如果表已存在,需要迁移数据 # 1. 创建新表(UUID v7 主键) # 2. 迁移数据 # 3. 删除旧表 pass ``` ### 3. 代码更新 所有调用 `SmsService` 的地方需要更新: ```python # 旧 await sms_service.send_verification_code( phone="13800138000", purpose="login" # 字符串 ) # 新 from app.models.sms import SmsPurpose await sms_service.send_verification_code( phone="13800138000", purpose=SmsPurpose.LOGIN # 枚举 ) ``` --- ## 性能影响 ### 预期提升 - **Redis 异步化**:减少阻塞,提升并发能力 - **HTTP 异步化**:短信发送不阻塞其他请求 - **条件索引**:查询未验证记录性能提升 30%+ ### 资源消耗 - 内存:基本无变化 - CPU:略微降低(异步 I/O 更高效) --- ## 测试建议 ### 单元测试 ```python @pytest.mark.asyncio async def test_send_verification_code(): # 测试异步发送 result = await sms_service.send_verification_code( phone="13800138000", purpose=SmsPurpose.LOGIN ) assert result["message"] == "验证码已发送" ``` ### 集成测试 - 测试 Redis 异步限流 - 测试 HTTP 异步调用 - 测试枚举类型转换 --- ## 相关文档 - [Jointo 技术栈规范](../../../architecture/tech-stack.md) - [后端异步编程规范](.claude/skills/jointo-tech-stack/references/backend.md) - [数据库设计规范](.claude/skills/jointo-tech-stack/references/database.md) - [API 设计规范](.claude/skills/jointo-tech-stack/references/api-design.md) --- **变更类型**:文档修正 + 架构优化 **影响范围**:短信验证码服务 **向后兼容**:❌ 需要迁移(主键类型变更) **优先级**:高(规范符合性)