543 lines
16 KiB
Markdown
543 lines
16 KiB
Markdown
# Phase 4: SAAS 产品化 - 设计文档
|
||
|
||
> 版本:v1.0
|
||
> 创建时间:2026-06-17
|
||
> 负责人:小虾 🦐
|
||
> 目标完成日期:2026-07-15
|
||
|
||
---
|
||
|
||
## 📋 目标
|
||
|
||
将小虾 SaaS 从技术 Demo 升级为真正的多租户商业化产品:
|
||
- ✅ 用户可以注册/登录/管理账号
|
||
- ✅ 支持多租户隔离和权限控制
|
||
- ✅ 支持订阅套餐和在线支付
|
||
|
||
---
|
||
|
||
## 🎯 核心功能模块
|
||
|
||
### 1️⃣ 认证与账号体系
|
||
|
||
#### 功能列表
|
||
- **用户注册**
|
||
- 邮箱 + 密码注册
|
||
- 邮箱验证(发送验证链接)
|
||
- 用户名唯一性检查
|
||
|
||
- **用户登录**
|
||
- 邮箱/用户名 + 密码登录
|
||
- JWT Token 签发(access_token + refresh_token)
|
||
- 记住我(30 天有效期)
|
||
|
||
- **密码管理**
|
||
- 忘记密码(邮件重置链接)
|
||
- 修改密码
|
||
- 密码强度要求(最少 8 位,包含字母+数字)
|
||
|
||
- **Session 管理**
|
||
- 刷新 Token
|
||
- 登出(撤销 Token)
|
||
- 查看活跃 Session(设备/IP/时间)
|
||
- 强制登出所有设备
|
||
|
||
#### 技术方案
|
||
|
||
**密码存储**
|
||
- 使用 `bcrypt` 哈希密码(cost=12)
|
||
- 不存储明文密码
|
||
- 密码哈希存储在 `User.password_hash` 字段
|
||
|
||
**JWT Token 设计**
|
||
```json
|
||
{
|
||
"access_token": {
|
||
"payload": {
|
||
"user_id": "uuid",
|
||
"workspace_id": "uuid",
|
||
"role": "owner|admin|member|viewer",
|
||
"exp": 1800 // 30 分钟过期
|
||
}
|
||
},
|
||
"refresh_token": {
|
||
"payload": {
|
||
"user_id": "uuid",
|
||
"session_id": "uuid",
|
||
"exp": 2592000 // 30 天过期
|
||
},
|
||
"存储": "Redis (key: refresh_token:{session_id}, value: user_id)"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Token 刷新流程**
|
||
1. 客户端检测 `access_token` 即将过期(剩余 < 5 分钟)
|
||
2. 用 `refresh_token` 调用 `/auth/refresh`
|
||
3. 验证 `refresh_token` 在 Redis 中存在且未过期
|
||
4. 签发新的 `access_token`(可选:同时轮转 `refresh_token`)
|
||
|
||
**邮件服务**
|
||
- SMTP 配置(支持 Gmail/阿里云企业邮箱等)
|
||
- 邮件模板(欢迎邮件/验证邮件/密码重置)
|
||
- 异步发送(Celery 任务)
|
||
|
||
#### API 设计
|
||
|
||
```
|
||
POST /api/v1/auth/register # 注册
|
||
POST /api/v1/auth/login # 登录
|
||
POST /api/v1/auth/logout # 登出
|
||
POST /api/v1/auth/refresh # 刷新 Token
|
||
POST /api/v1/auth/password/forgot # 忘记密码
|
||
POST /api/v1/auth/password/reset # 重置密码
|
||
POST /api/v1/auth/password/change # 修改密码
|
||
GET /api/v1/auth/verify-email # 邮箱验证
|
||
GET /api/v1/auth/sessions # 查看活跃 Session
|
||
DELETE /api/v1/auth/sessions/:id # 删除指定 Session
|
||
DELETE /api/v1/auth/sessions/all # 登出所有设备
|
||
```
|
||
|
||
#### 数据模型扩展
|
||
|
||
**User 实体新增字段**
|
||
```python
|
||
class User(BaseEntity):
|
||
# 原有字段
|
||
username: str
|
||
email: str
|
||
display_name: str
|
||
|
||
# 新增字段
|
||
password_hash: str # bcrypt 哈希
|
||
email_verified: bool = False # 邮箱是否验证
|
||
email_verification_token: str # 邮箱验证令牌
|
||
password_reset_token: str # 密码重置令牌
|
||
password_reset_expires_at: datetime # 重置令牌过期时间
|
||
last_login_at: datetime # 最后登录时间
|
||
last_login_ip: str # 最后登录 IP
|
||
```
|
||
|
||
**新增 Session 实体**
|
||
```python
|
||
class Session(BaseEntity):
|
||
user_id: str # 所属用户
|
||
refresh_token_hash: str # refresh_token 哈希
|
||
device_info: str # 设备信息(User-Agent)
|
||
ip_address: str # 登录 IP
|
||
expires_at: datetime # 过期时间
|
||
last_active_at: datetime # 最后活跃时间
|
||
```
|
||
|
||
---
|
||
|
||
### 2️⃣ 多租户权限体系
|
||
|
||
#### 功能列表
|
||
- **Workspace 成员管理**
|
||
- 邀请成员(发送邀请邮件)
|
||
- 移除成员
|
||
- 转让 Workspace 所有权
|
||
|
||
- **角色与权限**
|
||
- Owner(所有者):完全控制权
|
||
- Admin(管理员):管理成员、项目、资产
|
||
- Member(成员):创建/编辑自己的内容
|
||
- Viewer(查看者):只读权限
|
||
|
||
- **数据隔离**
|
||
- 所有资源(Project/Task/Asset)必须关联 Workspace
|
||
- API 自动过滤非当前 Workspace 的数据
|
||
- 防止跨 Workspace 访问
|
||
|
||
#### 权限矩阵
|
||
|
||
| 操作 | Owner | Admin | Member | Viewer |
|
||
|------|-------|-------|--------|--------|
|
||
| 查看 Workspace 信息 | ✅ | ✅ | ✅ | ✅ |
|
||
| 修改 Workspace 信息 | ✅ | ✅ | ❌ | ❌ |
|
||
| 删除 Workspace | ✅ | ❌ | ❌ | ❌ |
|
||
| 邀请成员 | ✅ | ✅ | ❌ | ❌ |
|
||
| 移除成员 | ✅ | ✅ | ❌ | ❌ |
|
||
| 修改成员角色 | ✅ | ✅ | ❌ | ❌ |
|
||
| 转让所有权 | ✅ | ❌ | ❌ | ❌ |
|
||
| 创建项目 | ✅ | ✅ | ✅ | ❌ |
|
||
| 编辑项目 | ✅ | ✅ | 自己的 | ❌ |
|
||
| 删除项目 | ✅ | ✅ | 自己的 | ❌ |
|
||
| 上传素材 | ✅ | ✅ | ✅ | ❌ |
|
||
| 查看素材 | ✅ | ✅ | ✅ | ✅ |
|
||
| 删除素材 | ✅ | ✅ | 自己的 | ❌ |
|
||
| 查看订阅/账单 | ✅ | ✅ | ❌ | ❌ |
|
||
| 修改订阅 | ✅ | ❌ | ❌ | ❌ |
|
||
|
||
#### 技术方案
|
||
|
||
**权限检查中间件**
|
||
```python
|
||
@require_permission("project:create")
|
||
async def create_project(request, ...):
|
||
pass
|
||
```
|
||
|
||
**权限装饰器实现**
|
||
```python
|
||
def require_permission(permission: str):
|
||
def decorator(func):
|
||
async def wrapper(request, *args, **kwargs):
|
||
user = request.state.user
|
||
workspace_id = request.state.workspace_id
|
||
|
||
# 检查用户在当前 Workspace 的角色
|
||
membership = get_workspace_membership(user.id, workspace_id)
|
||
if not membership.has_permission(permission):
|
||
raise HTTPException(403, "Permission denied")
|
||
|
||
return await func(request, *args, **kwargs)
|
||
return wrapper
|
||
return decorator
|
||
```
|
||
|
||
**数据隔离过滤器**
|
||
```python
|
||
# 所有查询自动加上 workspace_id 过滤
|
||
def list_projects(workspace_id: str) -> List[Project]:
|
||
return project_repo.find_by_workspace(workspace_id)
|
||
```
|
||
|
||
#### API 设计
|
||
|
||
```
|
||
# Workspace 成员管理
|
||
GET /api/v1/workspaces/:id/members # 成员列表
|
||
POST /api/v1/workspaces/:id/members/invite # 邀请成员
|
||
DELETE /api/v1/workspaces/:id/members/:user_id # 移除成员
|
||
PATCH /api/v1/workspaces/:id/members/:user_id # 修改角色
|
||
POST /api/v1/workspaces/:id/transfer # 转让所有权
|
||
|
||
# 邀请管理
|
||
GET /api/v1/invitations # 我的邀请
|
||
POST /api/v1/invitations/:id/accept # 接受邀请
|
||
POST /api/v1/invitations/:id/decline # 拒绝邀请
|
||
```
|
||
|
||
#### 数据模型扩展
|
||
|
||
**新增 WorkspaceMembership 实体**
|
||
```python
|
||
class WorkspaceMembership(BaseEntity):
|
||
workspace_id: str # 所属 Workspace
|
||
user_id: str # 成员用户
|
||
role: WorkspaceRole # 角色(owner/admin/member/viewer)
|
||
invited_by_user_id: str # 邀请人
|
||
joined_at: datetime # 加入时间
|
||
```
|
||
|
||
**新增 WorkspaceInvitation 实体**
|
||
```python
|
||
class WorkspaceInvitation(BaseEntity):
|
||
workspace_id: str # 邀请加入的 Workspace
|
||
email: str # 受邀邮箱
|
||
role: WorkspaceRole # 邀请角色
|
||
invited_by_user_id: str # 邀请人
|
||
token: str # 邀请令牌
|
||
expires_at: datetime # 过期时间
|
||
status: InvitationStatus # pending/accepted/declined/expired
|
||
```
|
||
|
||
---
|
||
|
||
### 3️⃣ 订阅与计费体系
|
||
|
||
#### 功能列表
|
||
- **订阅套餐**
|
||
- 免费版:1 个 Workspace,3 个项目,10GB 存储
|
||
- 专业版:5 个 Workspace,无限项目,100GB 存储,¥99/月
|
||
- 企业版:无限 Workspace,无限项目,1TB 存储,专属支持,¥999/月
|
||
|
||
- **支付功能**
|
||
- 支付宝扫码支付
|
||
- 微信扫码支付
|
||
- 自动续费(可选)
|
||
|
||
- **账单管理**
|
||
- 订阅历史
|
||
- 发票申请
|
||
- 退款申请
|
||
|
||
#### 技术方案
|
||
|
||
**支付流程**
|
||
1. 用户选择套餐 → 创建订单
|
||
2. 调用支付宝/微信支付 API → 生成支付二维码
|
||
3. 用户扫码支付 → 支付平台回调我们的 webhook
|
||
4. 验证回调签名 → 更新订单状态 → 激活订阅
|
||
|
||
**订阅状态机**
|
||
```
|
||
trial (试用) → active (有效) → expired (过期) → cancelled (取消)
|
||
↓
|
||
grace_period (宽限期,3 天)
|
||
```
|
||
|
||
**配额检查**
|
||
- 创建 Workspace 前检查套餐限制
|
||
- 上传文件前检查存储空间
|
||
- 超出配额时提示升级
|
||
|
||
#### API 设计
|
||
|
||
```
|
||
# 订阅管理
|
||
GET /api/v1/subscriptions/plans # 套餐列表
|
||
GET /api/v1/subscriptions/current # 当前订阅
|
||
POST /api/v1/subscriptions/subscribe # 订阅套餐
|
||
POST /api/v1/subscriptions/cancel # 取消订阅
|
||
POST /api/v1/subscriptions/renew # 续费
|
||
|
||
# 支付
|
||
POST /api/v1/payments/create # 创建支付订单
|
||
GET /api/v1/payments/:id # 查询订单状态
|
||
POST /api/v1/payments/webhook/alipay # 支付宝回调
|
||
POST /api/v1/payments/webhook/wechat # 微信回调
|
||
|
||
# 账单
|
||
GET /api/v1/invoices # 账单列表
|
||
GET /api/v1/invoices/:id # 账单详情
|
||
POST /api/v1/invoices/:id/download # 下载账单
|
||
```
|
||
|
||
#### 数据模型
|
||
|
||
**新增 Subscription 实体**
|
||
```python
|
||
class Subscription(BaseEntity):
|
||
workspace_id: str # 所属 Workspace
|
||
plan: SubscriptionPlan # 套餐(free/pro/enterprise)
|
||
status: SubscriptionStatus # 状态(trial/active/expired/cancelled)
|
||
started_at: datetime # 开始时间
|
||
expires_at: datetime # 到期时间
|
||
auto_renew: bool # 是否自动续费
|
||
```
|
||
|
||
**新增 Payment 实体**
|
||
```python
|
||
class Payment(BaseEntity):
|
||
workspace_id: str # 所属 Workspace
|
||
subscription_id: str # 关联订阅
|
||
amount: Decimal # 金额
|
||
currency: str = "CNY" # 货币
|
||
payment_method: PaymentMethod # alipay/wechat
|
||
status: PaymentStatus # pending/paid/failed/refunded
|
||
transaction_id: str # 支付平台交易号
|
||
paid_at: datetime # 支付时间
|
||
```
|
||
|
||
**新增 Invoice 实体**
|
||
```python
|
||
class Invoice(BaseEntity):
|
||
workspace_id: str # 所属 Workspace
|
||
payment_id: str # 关联支付
|
||
invoice_number: str # 发票号
|
||
amount: Decimal # 金额
|
||
tax_amount: Decimal # 税额
|
||
issued_at: datetime # 开票时间
|
||
pdf_url: str # PDF 下载地址
|
||
```
|
||
|
||
---
|
||
|
||
## 📐 开发规则与约定
|
||
|
||
### 代码规范
|
||
1. **遵循 Clean Architecture**
|
||
- 认证逻辑在 `packages/domain/auth/`
|
||
- Use Cases 在 `packages/application/auth/`
|
||
- API 路由在 `apps/api/app/api/routes/auth.py`
|
||
|
||
2. **安全第一**
|
||
- 所有密码必须哈希存储
|
||
- JWT Secret 从环境变量读取,不硬编码
|
||
- 敏感 API(修改密码/删除账号)需要二次验证
|
||
- 所有输入必须验证和清洗
|
||
|
||
3. **测试覆盖**
|
||
- 每个 Use Case 至少 1 个单元测试
|
||
- 每个 API 端点至少 1 个集成测试
|
||
- 安全相关功能(认证/权限)测试覆盖率 > 80%
|
||
|
||
4. **日志与监控**
|
||
- 登录/登出/权限拒绝必须记录日志
|
||
- 敏感操作(修改密码/删除账号)记录审计日志
|
||
- 异常情况告警(大量登录失败/异常 IP)
|
||
|
||
### Git 工作流
|
||
- 每个功能模块一个分支(如 `feature/auth-system`)
|
||
- 完成后合并到 `main`
|
||
- 提交信息格式:`feat(auth): add JWT login endpoint`
|
||
|
||
### 部署策略
|
||
- 认证功能先在测试环境验证
|
||
- 灰度发布(10% 流量 → 50% → 100%)
|
||
- 保留回滚能力(旧版本镜像保留 7 天)
|
||
|
||
---
|
||
|
||
## 📅 任务拆解与排期
|
||
|
||
### Milestone 1: 认证与账号体系(7 天,2026-06-17 ~ 2026-06-23)
|
||
|
||
**Day 1-2:基础设施搭建**
|
||
- [ ] JWT 工具类实现(sign/verify/refresh)
|
||
- [ ] bcrypt 密码哈希工具
|
||
- [ ] Redis Session 存储
|
||
- [ ] 邮件服务封装(SMTP + 模板)
|
||
- [ ] User 实体扩展(密码字段)
|
||
|
||
**Day 3-4:核心认证功能**
|
||
- [ ] 注册 API(邮箱验证)
|
||
- [ ] 登录 API(JWT 签发)
|
||
- [ ] 登出 API(撤销 Token)
|
||
- [ ] 刷新 Token API
|
||
- [ ] 密码重置流程
|
||
|
||
**Day 5-6:Session 管理**
|
||
- [ ] Session 实体与 Repository
|
||
- [ ] 活跃 Session 列表
|
||
- [ ] 强制登出所有设备
|
||
- [ ] 设备信息解析(User-Agent)
|
||
|
||
**Day 7:测试与文档**
|
||
- [ ] 集成测试(注册/登录/登出/刷新)
|
||
- [ ] 安全测试(密码强度/Token 伪造)
|
||
- [ ] API 文档更新
|
||
|
||
---
|
||
|
||
### Milestone 2: 多租户权限体系(7 天,2026-06-24 ~ 2026-06-30)
|
||
|
||
**Day 1-2:权限基础**
|
||
- [ ] WorkspaceMembership 实体与 Repository
|
||
- [ ] WorkspaceRole 枚举与权限定义
|
||
- [ ] 权限检查中间件
|
||
- [ ] 数据隔离过滤器
|
||
|
||
**Day 3-4:成员管理**
|
||
- [ ] 邀请成员 API
|
||
- [ ] 接受/拒绝邀请
|
||
- [ ] 移除成员
|
||
- [ ] 修改成员角色
|
||
- [ ] 转让所有权
|
||
|
||
**Day 5-6:权限验证**
|
||
- [ ] 所有现有 API 加上权限检查
|
||
- [ ] 跨 Workspace 访问防护测试
|
||
- [ ] 权限矩阵验证
|
||
|
||
**Day 7:测试与文档**
|
||
- [ ] 权限测试(各角色权限边界)
|
||
- [ ] 数据隔离测试
|
||
- [ ] API 文档更新
|
||
|
||
---
|
||
|
||
### Milestone 3: 订阅与计费(7 天,2026-07-01 ~ 2026-07-07)
|
||
|
||
**Day 1-2:订阅基础**
|
||
- [ ] Subscription 实体与 Repository
|
||
- [ ] SubscriptionPlan 枚举(free/pro/enterprise)
|
||
- [ ] 配额检查工具
|
||
- [ ] 套餐限制中间件
|
||
|
||
**Day 3-4:支付集成**
|
||
- [ ] 支付宝 SDK 集成
|
||
- [ ] 微信支付 SDK 集成
|
||
- [ ] 创建支付订单 API
|
||
- [ ] 支付回调处理(webhook)
|
||
|
||
**Day 5-6:账单管理**
|
||
- [ ] Invoice 实体与 Repository
|
||
- [ ] 生成账单 PDF
|
||
- [ ] 账单列表/下载 API
|
||
- [ ] 订阅历史记录
|
||
|
||
**Day 7:测试与文档**
|
||
- [ ] 支付流程端到端测试
|
||
- [ ] 配额检查测试
|
||
- [ ] API 文档更新
|
||
|
||
---
|
||
|
||
### Milestone 4: 前端集成与收尾(7 天,2026-07-08 ~ 2026-07-14)
|
||
|
||
**Day 1-2:认证 UI**
|
||
- [ ] 登录页面
|
||
- [ ] 注册页面
|
||
- [ ] 忘记密码页面
|
||
- [ ] 邮箱验证提示
|
||
|
||
**Day 3-4:权限 UI**
|
||
- [ ] 成员管理页面
|
||
- [ ] 邀请成员弹窗
|
||
- [ ] 角色选择器
|
||
- [ ] 权限说明文档
|
||
|
||
**Day 5-6:订阅 UI**
|
||
- [ ] 套餐选择页面
|
||
- [ ] 支付二维码页面
|
||
- [ ] 账单管理页面
|
||
- [ ] 配额使用展示
|
||
|
||
**Day 7:上线准备**
|
||
- [ ] 端到端测试
|
||
- [ ] 性能测试(登录/权限检查)
|
||
- [ ] 安全审计
|
||
- [ ] 生产环境部署
|
||
|
||
---
|
||
|
||
## 🔒 安全检查清单
|
||
|
||
### 认证安全
|
||
- [ ] 密码哈希使用 bcrypt,cost ≥ 12
|
||
- [ ] JWT Secret 强度足够(≥ 32 字节随机)
|
||
- [ ] Token 过期时间合理(access 30 分钟,refresh 30 天)
|
||
- [ ] 防止暴力破解(登录失败 5 次锁定 15 分钟)
|
||
- [ ] 防止用户枚举(注册/登录错误信息统一)
|
||
|
||
### 数据安全
|
||
- [ ] 所有查询强制 Workspace 过滤
|
||
- [ ] 跨 Workspace 访问返回 403,不是 404
|
||
- [ ] 敏感字段(密码哈希)不出现在 API 响应
|
||
- [ ] SQL 注入防护(使用 ORM,参数化查询)
|
||
- [ ] XSS 防护(输入清洗,输出转义)
|
||
|
||
### 支付安全
|
||
- [ ] 回调验证签名
|
||
- [ ] 订单金额不信任客户端
|
||
- [ ] 防止重放攻击(nonce + timestamp)
|
||
- [ ] 支付密钥不出现在日志
|
||
|
||
---
|
||
|
||
## 📊 成功指标
|
||
|
||
- [ ] 用户可以完整走通注册 → 登录 → 使用 → 续费流程
|
||
- [ ] 权限系统无漏洞(各角色权限边界清晰)
|
||
- [ ] 支付成功率 > 95%
|
||
- [ ] 认证 API 响应时间 < 200ms
|
||
- [ ] 权限检查开销 < 10ms
|
||
- [ ] 安全测试通过(无 SQL 注入/XSS/CSRF)
|
||
|
||
---
|
||
|
||
## 📝 备注
|
||
|
||
- 邮件服务可以先用 SMTP(Gmail/阿里云邮箱),后期再考虑 SendGrid
|
||
- 支付回调需要公网可访问地址(备案通过后才能配置)
|
||
- 支付功能可以先用沙箱环境测试
|
||
- 订阅计费逻辑需要 cron 定时任务检查到期(每小时一次)
|
||
|
||
---
|
||
|
||
**老大确认后,我就把这些任务同步到推进器里。**
|