Your Name f87ae48ad5 feat: 实现 FastAPI REST API 端点和集成测试
- 添加认证 API (登录/token验证)
- 添加 Brief API (上传/解析/导入/冲突检测)
- 添加视频 API (上传/断点续传/审核/违规/预览/重提交)
- 添加审核 API (决策/批量审核/申诉/历史)
- 实现基于角色的权限控制
- 更新集成测试,49 个测试全部通过
- 总体测试覆盖率 89.63%

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:08:12 +08:00

145 lines
3.5 KiB
Python

"""
认证 API 端点
"""
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import datetime, timedelta
import secrets
router = APIRouter()
# 模拟用户数据库
MOCK_USERS = {
"agency@test.com": {
"user_id": "user_agency_001",
"email": "agency@test.com",
"password": "password",
"role": "agency",
"appeal_tokens": 5,
},
"creator@test.com": {
"user_id": "user_creator_001",
"email": "creator@test.com",
"password": "password",
"role": "creator",
"appeal_tokens": 3,
},
"reviewer@test.com": {
"user_id": "user_reviewer_001",
"email": "reviewer@test.com",
"password": "password",
"role": "reviewer",
"appeal_tokens": 0,
},
"brand@test.com": {
"user_id": "user_brand_001",
"email": "brand@test.com",
"password": "password",
"role": "brand",
"appeal_tokens": 0,
},
"no_token@test.com": {
"user_id": "user_no_token_001",
"email": "no_token@test.com",
"password": "password",
"role": "creator",
"appeal_tokens": 0,
},
}
# 模拟 token 存储
TOKENS: dict[str, dict] = {}
class LoginRequest(BaseModel):
email: EmailStr
password: str
class LoginResponse(BaseModel):
access_token: str
token_type: str = "bearer"
user_id: str
role: str
expires_in: int = 3600
class UserProfile(BaseModel):
user_id: str
email: str
role: str
appeal_tokens: int
@router.post("/login", response_model=LoginResponse)
async def login(request: LoginRequest):
"""用户登录"""
user = MOCK_USERS.get(request.email)
if not user or user["password"] != request.password:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid email or password",
)
# 生成 token
token = secrets.token_urlsafe(32)
TOKENS[token] = {
"user_id": user["user_id"],
"email": user["email"],
"role": user["role"],
"expires_at": datetime.now() + timedelta(hours=1),
}
return LoginResponse(
access_token=token,
user_id=user["user_id"],
role=user["role"],
)
def get_current_user(token: str) -> dict:
"""验证 token 并返回用户信息"""
if not token or not token.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authorization header",
)
token_value = token[7:] # 移除 "Bearer " 前缀
token_data = TOKENS.get(token_value)
if not token_data:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token",
)
if datetime.now() > token_data["expires_at"]:
del TOKENS[token_value]
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token expired",
)
return token_data
def get_user_by_id(user_id: str) -> dict | None:
"""根据 user_id 获取用户"""
for email, user in MOCK_USERS.items():
if user["user_id"] == user_id:
return user
return None
def update_user_tokens(user_id: str, delta: int) -> None:
"""更新用户申诉令牌"""
for email, user in MOCK_USERS.items():
if user["user_id"] == user_id:
user["appeal_tokens"] += delta
break