diff --git a/docs/01-PRD-产品需求文档.md b/docs/01-PRD-产品需求文档.md index f478092..0e8940d 100644 --- a/docs/01-PRD-产品需求文档.md +++ b/docs/01-PRD-产品需求文档.md @@ -68,16 +68,24 @@ AI PaaS 商业平台是一个 **AI 能力的"水电煤"基础设施**。用户 ### 3.1 注册与登录 #### 注册方式 -1. **手机号 + 验证码**(主推) +1. **手机号 + 短信验证码**(主推,短信宝 SMS Bao 接口) 2. **邮箱 + 密码**(备用) 3. **第三方登录**: 微信扫码(通过 Casdoor SSO 对接) #### 登录方式 -1. 手机号 + 验证码 +1. 手机号 + 短信验证码 2. 邮箱 + 密码 3. 微信扫码 4. API Key(程序化调用,无需登录态) +#### 短信服务 +- **服务商**: 短信宝 (SMS Bao) +- **接口**: https://api.smsbao.com/sms (GET) +- **验证码**: 6 位数字,5 分钟有效 +- **频率限制**: 同号码 60 秒 1 条,每天 10 条 +- **签名格式**: 【QihuAI】您的验证码是123456... +- **详细集成文档**: docs/smsbao-integration.md + ### 3.2 实名认证(阿里云接口) 用户需要完成实名认证才能发布能力。对接阿里云实名认证 API: diff --git a/docs/02-系统架构设计.md b/docs/02-系统架构设计.md index 7266f90..62645f6 100644 --- a/docs/02-系统架构设计.md +++ b/docs/02-系统架构设计.md @@ -26,6 +26,7 @@ | **实名认证** | 自建 OCR | 阿里云 API | **阿里云** | 国内最权威,接口稳定 | | **AI 服务框架** | Go 自研 | FastAPI | **FastAPI** | AI 生态标准,79k Stars,异步高性能 | | **本地 LLM** | 自建 | Ollama | **Ollama** | 130k Stars,本地部署事实标准 | +| **短信服务** | 阿里云短信 | 短信宝 | **短信宝** | 简单 HTTP GET 接口,快速接入 | | **API 网关** | 自研 | APISIX | **APISIX** | 高性能,插件丰富,国内团队 | | **数据库** | MySQL | PostgreSQL | **PostgreSQL** | JSONB + RLS,功能更强 | | **缓存** | Memcached | Redis | **Redis** | Lua 脚本支持,原子计费必备 | @@ -247,6 +248,25 @@ return 'OK:' .. new_balance ## 6. 开源组件集成方案 +### 6.0 SMS Bao (短信服务) + +``` +接入方式: HTTP GET 接口(无需 SDK) +接口地址: https://api.smsbao.com/sms +认证方式: ApiKey 或 MD5 密码 +配置文件: infra/smsbao/config.yaml + smsbao: + username: "your_username" + api_key: "your_api_key" # 优先使用 ApiKey + sign_name: "QihuAI" # 短信签名 + api_url: "https://api.smsbao.com/sms" + query_url: "http://api.smsbao.com/query" + code_length: 6 # 验证码长度 + code_ttl: 300 # 验证码有效期(秒) + rate_limit: 60 # 同号码发送间隔(秒) + daily_limit: 10 # 同号码每日上限 +``` + ### 6.1 Casdoor (SSO 认证中心) ``` diff --git a/docs/04-API接口规范.md b/docs/04-API接口规范.md index 19a2215..c6d6f39 100644 --- a/docs/04-API接口规范.md +++ b/docs/04-API接口规范.md @@ -46,9 +46,29 @@ ## 2. 认证接口(Casdoor 代理) +### POST /api/v1/auth/send-code +```yaml +description: 发送手机验证码(短信宝 SMS Bao) +request: + body: + phone: string (required) + captcha_id: string (required) # 图形验证码ID + captcha_code: string (required) # 图形验证码答案 +response: + 200: + data: + message: "验证码已发送" + 400: + code: 40001 + message: "图形验证码错误" + 429: + code: 42901 + message: "发送太频繁,请60秒后重试" +``` + ### POST /api/v1/auth/register ```yaml -description: 注册(手机号 + 验证码) +description: 注册(手机号 + 短信验证码) request: body: phone: string (required) @@ -64,11 +84,11 @@ response: ### POST /api/v1/auth/login ```yaml -description: 登录(手机号 + 验证码) +description: 登录(手机号 + 短信验证码) request: body: phone: string (required) - code: string (required) + code: string (required, 6位验证码) response: 200: data: diff --git a/docs/05-AGENTS-MiMoCode配置.md b/docs/05-AGENTS-MiMoCode配置.md index 3a3f4f5..5bf4750 100644 --- a/docs/05-AGENTS-MiMoCode配置.md +++ b/docs/05-AGENTS-MiMoCode配置.md @@ -22,9 +22,10 @@ - **API 网关**: APISIX (Lua, 开源) — 路由/限流/鉴权 - **本地 LLM**: Ollama (Go, 开源) — 本地模型推理 - **Agent 编排**: LangFlow (Python, 开源) — 可视化工作流 +- **短信服务**: 短信宝 SMS Bao (HTTP GET) — 验证码发送 - **前端**: React 18 + TypeScript + Ant Design Pro + ECharts - **数据库**: PostgreSQL 16 -- **缓存**: Redis 7 (Lua 原子计费) +- **缓存**: Redis 7 (Lua 原子计费 + 验证码存储) - **消息队列**: Kafka 3.x (用量上报) - **实名认证**: 阿里云实名认证 API @@ -145,6 +146,15 @@ QihuAI/ - 流式: SSE (text/event-stream) - 百度需适配层 (NEW API 内置) +## 短信服务 (SMS Bao) +- 接口: https://api.smsbao.com/sms (GET) +- 参数: u(用户名), p(ApiKey或MD5密码), m(手机号), c(内容,URL编码) +- 验证码: 6位数字, 5分钟有效, Redis存储 +- 频率限制: 同号码60秒1条, 每天10条 +- 签名格式: 【QihuAI】您的验证码是... +- 安全: 发送前必须验证图形验证码(防轰炸) +- 详细集成: docs/smsbao-integration.md + ## 实名认证 - 阿里云实名认证 API - 用户提交: 真实姓名 + 身份证号 diff --git a/docs/smsbao-integration.md b/docs/smsbao-integration.md new file mode 100644 index 0000000..ece61d0 --- /dev/null +++ b/docs/smsbao-integration.md @@ -0,0 +1,292 @@ +# 短信宝 (SMS Bao) 接口集成文档 + +> 版本: 1.0 | 日期: 2026-06-27 +> 官网: https://www.smsbao.com | API 文档: https://www.smsbao.com/openapi + +--- + +## 1. 接口概述 + +短信宝提供简洁的 HTTP GET 接口,用于发送国内短信验证码。 + +### 1.1 接口地址 + +| 功能 | 地址 | 方法 | +|------|------|------| +| 发送短信 | `https://api.smsbao.com/sms` | GET | +| 查询余额 | `http://api.smsbao.com/query` | GET | + +### 1.2 请求参数(发送短信) + +| 参数 | 必填 | 说明 | 示例 | +|------|------|------|------| +| `u` | 是 | 短信宝注册用户名 | `tom` | +| `p` | 是 | 密码 MD5 值(32位)**或** ApiKey(推荐) | `9b11127a9701975c734b8aee81ee3526` | +| `g` | 否 | 产品 ID(专用通道时填,不填用通用短信) | `123456` | +| `m` | 是 | 手机号(群发逗号分隔,≤99个) | `15205201314` | +| `c` | 是 | 短信内容(UTF-8 URL 编码,须含签名) | `%E3%80%90...%E3%80%91` | + +### 1.3 返回值 + +| 返回值 | 含义 | +|--------|------| +| `0` | 发送成功 | +| 其他 | 错误代码(见下方错误码表) | + +### 1.4 查询余额参数 + +| 参数 | 必填 | 说明 | +|------|------|------| +| `u` | 是 | 用户名 | +| `p` | 是 | 密码 MD5 或 ApiKey | + +返回: 数字字符串,如 `"523"` 表示剩余 523 条短信。 + +--- + +## 2. 错误码 + +| 错误码 | 含义 | +|--------|------| +| `0` | 成功 | +| `40` | 手机号格式错误 | +| `41` | 手机号为空 | +| `42` | 内容为空 | +| `43` | 用户名或密码错误 | +| `44` | 账户余额不足 | +| `45` | 签名格式错误(必须含【签名】) | +| `46` | 内容包含敏感词 | +| `47` | 频率超限(同号码4条/天) | +| `50` | 账号被封禁 | + +--- + +## 3. 短信内容规范 + +### 3.1 签名格式 +``` +【QihuAI】您的验证码是123456,5分钟内有效。若非本人操作请忽略。 +``` + +- 签名在 `【】` 中间,3-10 个字符 +- 签名可为公司简称、网站简称、注册商标名称 +- 签名需在短信宝后台报备审核 + +### 3.2 测试阶段注意事项 +- 测试时也需发送正规内容(验证码、通知等) +- 不要发送无意义内容(如"测试"、"你好") +- 同号码每天限收 4 条(可在后台白名单取消限制) +- 群发短信会进入人工审核 + +--- + +## 4. FastAPI 集成代码 + +### 4.1 配置 (.env) + +```env +# 短信宝配置 +SMSBAO_USERNAME=your_username +SMSBAO_API_KEY=your_api_key_here +SMSBAO_PRODUCT_ID= # 可选,专用通道产品ID +SMSBAO_SIGN_NAME=QihuAI # 短信签名 +SMSBAO_API_URL=https://api.smsbao.com/sms +SMSBAO_QUERY_URL=http://api.smsbao.com/query +``` + +### 4.2 核心服务代码 + +```python +# services/gateway/smsbao.py + +import hashlib +from urllib.parse import quote +from pydantic import BaseModel +import httpx +from core.config import settings + + +class SMSBaoClient: + """短信宝短信发送客户端""" + + def __init__(self): + self.api_url = settings.SMSBAO_API_URL + self.query_url = settings.SMSBAO_QUERY_URL + self.username = settings.SMSBAO_USERNAME + self.api_key = settings.SMSBAO_API_KEY + self.product_id = settings.SMSBAO_PRODUCT_ID + self.sign_name = settings.SMSBAO_SIGN_NAME + + def _get_password(self) -> str: + """获取认证密码(优先使用 ApiKey,否则 MD5 密码)""" + if self.api_key: + return self.api_key + # 如果没有 ApiKey,对密码做 MD5 + return hashlib.md5(settings.SMSBAO_PASSWORD.encode()).hexdigest() + + async def send_sms(self, phone: str, content: str) -> dict: + """ + 发送短信 + :param phone: 手机号 + :param content: 短信内容(不含签名,会自动添加) + :return: {"success": bool, "message": str} + """ + # 自动添加签名 + full_content = f"【{self.sign_name}】{content}" + encoded_content = quote(full_content, encoding="utf-8") + + params = { + "u": self.username, + "p": self._get_password(), + "m": phone, + "c": encoded_content, + } + if self.product_id: + params["g"] = self.product_id + + async with httpx.AsyncClient(timeout=10.0) as client: + resp = await client.get(self.api_url, params=params) + result = resp.text.strip() + + if result == "0": + return {"success": True, "message": "发送成功"} + else: + return {"success": False, "message": f"发送失败,错误码: {result}"} + + async def send_verification_code(self, phone: str, code: str) -> dict: + """发送验证码短信""" + content = f"您的验证码是{code},5分钟内有效。若非本人操作请忽略。" + return await self.send_sms(phone, content) + + async def query_balance(self) -> dict: + """查询余额""" + params = { + "u": self.username, + "p": self._get_password(), + } + async with httpx.AsyncClient(timeout=10.0) as client: + resp = await client.get(self.query_url, params=params) + result = resp.text.strip() + + try: + balance = int(result) + return {"success": True, "balance": balance} + except ValueError: + return {"success": False, "message": f"查询失败: {result}"} + + +# 单例 +smsbao_client = SMSBaoClient() +``` + +### 4.3 验证码服务 + +```python +# services/gateway/verification.py + +import random +import string +from datetime import datetime, timedelta +from redis import asyncio as redis + +redis_client = redis.from_url("redis://localhost:6379/0") + +VERIFICATION_CODE_TTL = 300 # 5 分钟 +VERIFICATION_CODE_LENGTH = 6 +RATE_LIMIT_PER_PHONE = 1 # 每分钟最多 1 条 + + +async def generate_and_send_code(phone: str) -> dict: + """ + 生成验证码并发送 + 1. 检查频率限制 + 2. 生成 6 位随机验证码 + 3. 存入 Redis(5分钟过期) + 4. 调用短信宝发送 + """ + # 频率限制:同号码 60 秒内不能重复发送 + rate_key = f"sms:rate:{phone}" + if await redis_client.exists(rate_key): + return {"success": False, "message": "发送太频繁,请60秒后重试"} + + # 生成验证码 + code = "".join(random.choices(string.digits, k=VERIFICATION_CODE_LENGTH)) + + # 存入 Redis + code_key = f"sms:code:{phone}" + await redis_client.setex(code_key, VERIFICATION_CODE_TTL, code) + await redis_client.setex(rate_key, 60, "1") + + # 发送 + from smsbao import smsbao_client + result = await smsbao_client.send_verification_code(phone, code) + + if result["success"]: + return {"success": True, "message": "验证码已发送"} + else: + return {"success": False, "message": result["message"]} + + +async def verify_code(phone: str, code: str) -> bool: + """验证验证码是否正确""" + code_key = f"sms:code:{phone}" + stored_code = await redis_client.get(code_key) + if stored_code and stored_code.decode() == code: + await redis_client.delete(code_key) # 验证通过后删除 + return True + return False +``` + +### 4.4 API 路由 + +```python +# services/gateway/routes/auth.py + +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel +from verification import generate_and_send_code, verify_code + +router = APIRouter(prefix="/api/v1/auth", tags=["认证"]) + + +class SendCodeRequest(BaseModel): + phone: str + + +class VerifyCodeRequest(BaseModel): + phone: str + code: str + + +@router.post("/send-code") +async def send_verification_code(req: SendCodeRequest): + """发送手机验证码""" + result = await generate_and_send_code(req.phone) + if not result["success"]: + raise HTTPException(status_code=400, detail=result["message"]) + return {"code": 0, "message": "验证码已发送", "data": None} + + +@router.post("/verify-code") +async def verify_phone_code(req: VerifyCodeRequest): + """验证手机验证码""" + valid = await verify_code(req.phone, req.code) + if not valid: + raise HTTPException(status_code=400, detail="验证码错误或已过期") + return {"code": 0, "message": "验证成功", "data": {"verified": True}} +``` + +--- + +## 5. 安全注意事项 + +1. **图形验证码**: 发送短信前必须验证图形验证码(防短信轰炸) +2. **频率限制**: 同号码 60 秒内只能发 1 条 +3. **每日限制**: 同号码每天最多 10 条(可在短信宝后台调整) +4. **密码安全**: ApiKey / MD5 密码存储在 .env,不提交到 Git +5. **HTTPS**: 使用安全接口 `https://api.smsbao.com/sms` +6. **白名单**: 测试阶段在短信宝后台添加测试手机号白名单 + +--- + +*文档结束*