Files
social-app/docs/plans/2026-02-26-restful-api-refactor-plan.md
T

1514 lines
31 KiB
Markdown
Raw Normal View History

# RESTful API 重构实现计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 将后端 HTTP API 改造为完全符合 RESTful 规范,同步更新前端适配代码,并添加路由文档。
**Architecture:**
- 后端:重命名路由 URL,简化响应模型,profile 模块重命名为 users
- 前端:更新 API 调用路径,简化响应模型
- 文档:新增路由文档,更新 AGENTS.md 规则
**Tech Stack:** FastAPI, Pydantic, Flutter, Dart
---
## Phase 1: 后端 Schema 重构
### Task 1: 重命名和简化 Auth Schema
**Files:**
- Modify: `backend/src/v1/auth/schemas.py`
**Step 1: 更新 schema 文件**
将现有 schema 重命名并简化:
```python
from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, EmailStr, Field
class VerificationCreateRequest(BaseModel):
username: str = Field(min_length=3, max_length=30)
email: EmailStr
password: str = Field(min_length=6)
redirect_to: str | None = None
class VerificationResendRequest(BaseModel):
email: EmailStr
class VerificationVerifyRequest(BaseModel):
email: EmailStr
token: str = Field(pattern=r"^\d{6}$")
class SessionCreateRequest(BaseModel):
email: EmailStr
password: str = Field(min_length=6)
class SessionRefreshRequest(BaseModel):
refresh_token: str = Field(min_length=1)
class SessionDeleteRequest(BaseModel):
refresh_token: str = Field(min_length=1)
class AuthUser(BaseModel):
id: str
email: EmailStr
class SessionResponse(BaseModel):
access_token: str
refresh_token: str
expires_in: int
token_type: str
user: AuthUser
class UserByEmailResponse(BaseModel):
id: str
email: EmailStr
created_at: str
email_confirmed_at: str | None = None
class VerificationCreateResponse(BaseModel):
email: EmailStr
class PasswordResetRequest(BaseModel):
email: EmailStr
redirect_to: str | None = None
class PasswordResetResponse(BaseModel):
message: str = "Password reset email sent"
```
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/auth/schemas.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/auth/schemas.py
git commit -m "refactor(auth): rename and simplify auth schemas for RESTful API"
```
---
### Task 2: 创建 Users Schema
**Files:**
- Create: `backend/src/v1/users/schemas.py`
**Step 1: 创建 users schema 文件**
```python
from __future__ import annotations
from pydantic import (
AnyHttpUrl,
BaseModel,
ConfigDict,
Field,
field_validator,
model_validator,
)
class UserResponse(BaseModel):
id: str
username: str
avatar_url: str | None = None
bio: str | None = None
class UserUpdateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
username: str | None = Field(default=None, min_length=3, max_length=30)
avatar_url: str | None = Field(default=None)
bio: str | None = Field(default=None, max_length=200)
@field_validator("avatar_url", mode="before")
@classmethod
def validate_avatar_url(cls, v: str | None) -> str | None:
if v is None:
return None
parsed = AnyHttpUrl(v)
if parsed.scheme not in ("http", "https"):
raise ValueError("avatar_url must use http or https scheme")
return str(parsed)
@model_validator(mode="after")
def require_one_field(self) -> "UserUpdateRequest":
if self.username is None and self.avatar_url is None and self.bio is None:
raise ValueError("At least one field must be provided")
return self
```
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/users/schemas.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/users/schemas.py
git commit -m "feat(users): create users schemas"
```
---
### Task 3: 创建 Users 模块基础文件
**Files:**
- Create: `backend/src/v1/users/__init__.py`
- Create: `backend/src/v1/users/dependencies.py`
- Move: `backend/src/v1/profile/repository.py``backend/src/v1/users/repository.py`
- Move: `backend/src/v1/profile/service.py``backend/src/v1/users/service.py`
**Step 1: 创建 __init__.py**
```python
from __future__ import annotations
```
**Step 2: 创建 dependencies.py**
```python
from __future__ import annotations
from typing import Annotated
from fastapi import Depends
from core.auth.models import CurrentUser
from core.db import get_db
from v1.auth.dependencies import get_current_user
from v1.users.repository import UserRepository
from v1.users.service import UserService
async def get_user_repository(
db=Depends(get_db),
) -> UserRepository:
return UserRepository(db)
async def get_user_service(
repo: Annotated[UserRepository, Depends(get_user_repository)],
current_user: Annotated[CurrentUser, Depends(get_current_user)],
) -> UserService:
return UserService(repo, current_user)
```
**Step 3: 移动并更新 repository.py**
复制 `v1/profile/repository.py``v1/users/repository.py`,无需修改内容。
**Step 4: 移动并更新 service.py**
复制 `v1/profile/service.py``v1/users/service.py`,更新导入:
```python
from v1.users.schemas import UserResponse, UserUpdateRequest
```
**Step 5: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/users/`
Expected: No errors
**Step 4: Commit**
```bash
git add backend/src/v1/users/
git commit -m "feat(users): create users module from profile"
```
---
## Phase 2: 后端路由重构
### Task 4: 重构 Auth Router
**Files:**
- Modify: `backend/src/v1/auth/router.py`
**Step 1: 更新路由定义**
```python
from __future__ import annotations
from typing import Annotated
from fastapi import APIRouter, Depends, Response
from fastapi import HTTPException
from core.auth.models import CurrentUser
from v1.auth.rate_limit import enforce_rate_limit
from v1.auth.dependencies import get_auth_service
from v1.users.dependencies import get_current_user
from v1.auth.schemas import (
VerificationCreateRequest,
VerificationCreateResponse,
VerificationResendRequest,
VerificationVerifyRequest,
SessionCreateRequest,
SessionDeleteRequest,
SessionRefreshRequest,
SessionResponse,
UserByEmailResponse,
)
from v1.auth.service import AuthService
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/verifications", response_model=VerificationCreateResponse, status_code=202)
async def create_verification(
payload: VerificationCreateRequest,
service: AuthService = Depends(get_auth_service),
) -> VerificationCreateResponse:
await enforce_rate_limit(
scope="signup_start",
identifier=payload.email,
limit=5,
window_seconds=60,
)
return await service.create_verification(payload)
@router.post("/verifications/resend", status_code=204)
async def resend_verification(
payload: VerificationResendRequest,
service: AuthService = Depends(get_auth_service),
) -> Response:
await enforce_rate_limit(
scope="signup_resend",
identifier=payload.email,
limit=5,
window_seconds=60,
)
await service.resend_verification(payload)
return Response(status_code=204)
@router.post("/verifications/verify", response_model=SessionResponse)
async def verify_verification(
payload: VerificationVerifyRequest,
service: AuthService = Depends(get_auth_service),
) -> SessionResponse:
await enforce_rate_limit(
scope="signup_verify",
identifier=payload.email,
limit=10,
window_seconds=600,
)
return await service.verify_verification(payload)
@router.post("/sessions", response_model=SessionResponse)
async def create_session(
payload: SessionCreateRequest,
service: AuthService = Depends(get_auth_service),
) -> SessionResponse:
await enforce_rate_limit(
scope="login",
identifier=payload.email,
limit=10,
window_seconds=60,
)
return await service.create_session(payload)
@router.post("/sessions/refresh", response_model=SessionResponse)
async def refresh_session(
payload: SessionRefreshRequest,
service: AuthService = Depends(get_auth_service),
) -> SessionResponse:
await enforce_rate_limit(
scope="refresh",
identifier=payload.refresh_token,
limit=10,
window_seconds=60,
)
return await service.refresh_session(payload)
@router.delete("/sessions", status_code=204)
async def delete_session(
payload: SessionDeleteRequest,
service: AuthService = Depends(get_auth_service),
) -> Response:
await enforce_rate_limit(
scope="logout",
identifier=payload.refresh_token,
limit=10,
window_seconds=60,
)
await service.delete_session(payload.refresh_token)
return Response(status_code=204)
@router.get("/users", response_model=UserByEmailResponse)
async def get_user_by_email(
email: str,
current_user: Annotated[CurrentUser, Depends(get_current_user)],
service: AuthService = Depends(get_auth_service),
) -> UserByEmailResponse:
if current_user.role != "service_role" and current_user.email != email:
raise HTTPException(status_code=403, detail="Forbidden")
return await service.get_user_by_email(email)
```
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/auth/router.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/auth/router.py
git commit -m "refactor(auth): rename routes to RESTful style"
```
---
### Task 5: 重构 Auth Service
**Files:**
- Modify: `backend/src/v1/auth/service.py`
**Step 1: 更新 service 方法名和导入**
更新导入和方法名以匹配新的 schema:
```python
from v1.auth.schemas import (
VerificationCreateRequest,
VerificationCreateResponse,
VerificationResendRequest,
VerificationVerifyRequest,
SessionCreateRequest,
SessionDeleteRequest,
SessionRefreshRequest,
SessionResponse,
UserByEmailResponse,
)
```
将方法重命名:
- `signup_start``create_verification`
- `signup_resend``resend_verification`
- `signup_verify``verify_verification`
- `login``create_session`
- `refresh``refresh_session`
- `logout``delete_session`
`create_verification` 返回 `VerificationCreateResponse(email=payload.email)`
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/auth/service.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/auth/service.py
git commit -m "refactor(auth): rename service methods for RESTful API"
```
---
### Task 6: 更新 Auth Gateway
**Files:**
- Modify: `backend/src/v1/auth/gateway.py`
**Step 1: 更新导入和方法调用**
更新导入使用新 schema 名称,方法调用更新为新的 service 方法名。
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/auth/gateway.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/auth/gateway.py
git commit -m "refactor(auth): update gateway for new service methods"
```
---
### Task 7: 创建 Users Router
**Files:**
- Create: `backend/src/v1/users/router.py`
**Step 1: 创建 users router**
```python
from __future__ import annotations
from typing import Annotated
from fastapi import APIRouter, Depends, Path
from v1.users.dependencies import get_user_service
from v1.users.schemas import UserResponse, UserUpdateRequest
from v1.users.service import UserService
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/me", response_model=UserResponse)
async def get_me(
service: Annotated[UserService, Depends(get_user_service)],
) -> UserResponse:
return await service.get_me()
@router.patch("/me", response_model=UserResponse)
async def update_me(
payload: UserUpdateRequest,
service: Annotated[UserService, Depends(get_user_service)],
) -> UserResponse:
return await service.update_me(payload)
@router.get("/{username}", response_model=UserResponse)
async def get_by_username(
username: Annotated[
str, Path(min_length=3, max_length=30, pattern="^[a-zA-Z0-9_]+$")
],
service: Annotated[UserService, Depends(get_user_service)],
) -> UserResponse:
return await service.get_by_username(username)
```
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/users/router.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/users/router.py
git commit -m "feat(users): create users router"
```
---
### Task 8: 更新 Agent Chat Router
**Files:**
- Modify: `backend/src/v1/agent_chat/router.py`
**Step 1: 更新 URL 路径**
`/run` 改为根路径:
```python
@router.post("", response_model=AgentChatRunResponse)
async def run_agent_chat(
payload: AgentChatRunRequest,
service: Annotated[AgentChatService, Depends(get_agent_chat_service)],
) -> AgentChatRunResponse:
return await service.run(payload)
```
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/agent_chat/router.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/agent_chat/router.py
git commit -m "refactor(agent-chat): change route to RESTful style"
```
---
### Task 9: 更新主路由注册
**Files:**
- Modify: `backend/src/v1/router.py`
**Step 1: 更新路由注册**
```python
from __future__ import annotations
from fastapi import APIRouter
from core.http.models import HealthResponse
from v1.agent_chat.router import router as agent_chat_router
from v1.auth.router import router as auth_router
from v1.infra.router import router as infra_router
from v1.users.router import router as users_router
router = APIRouter(prefix="/api/v1")
router.include_router(auth_router)
router.include_router(infra_router)
router.include_router(users_router)
router.include_router(agent_chat_router)
@router.get("/health", response_model=HealthResponse)
async def health() -> HealthResponse:
return HealthResponse(status="ok")
```
**Step 2: 验证类型检查**
Run: `cd backend && uv run basedpyright src/v1/router.py`
Expected: No errors
**Step 3: Commit**
```bash
git add backend/src/v1/router.py
git commit -m "refactor: register users router instead of profile"
```
---
## Phase 3: 后端测试更新
### Task 10: 更新 Auth 路由测试
**Files:**
- Modify: `backend/tests/integration/test_auth_routes.py`
**Step 1: 更新测试用例的 URL 和断言**
- `/signup/start``/auth/verifications`
- `/signup/resend``/auth/verifications/resend`
- `/signup/verify``/auth/verifications/verify`
- `/login``/auth/sessions`
- `/refresh``/auth/sessions/refresh`
- `/logout``/auth/sessions`
- `/users/by-email``/auth/users?email=xxx`
更新断言:
- `signup_start` 响应只包含 `email` 字段
- `signup_resend` 响应为 204,无 body
**Step 2: 运行测试验证**
Run: `cd backend && uv run pytest tests/integration/test_auth_routes.py -v`
Expected: All tests pass
**Step 3: Commit**
```bash
git add backend/tests/integration/test_auth_routes.py
git commit -m "test(auth): update integration tests for RESTful routes"
```
---
### Task 11: 创建 Users 路由测试
**Files:**
- Move: `backend/tests/integration/test_profile_routes.py``backend/tests/integration/test_users_routes.py`
**Step 1: 移动并更新测试文件**
- 更新 URL`/profile/me``/users/me``/profile/{username}``/users/{username}`
- 更新导入和断言
**Step 2: 运行测试验证**
Run: `cd backend && uv run pytest tests/integration/test_users_routes.py -v`
Expected: All tests pass
**Step 3: Commit**
```bash
git add backend/tests/integration/test_users_routes.py
git rm backend/tests/integration/test_profile_routes.py
git commit -m "test(users): rename profile tests to users"
```
---
### Task 12: 运行完整测试套件
**Step 1: 运行所有后端测试**
Run: `cd backend && uv run pytest -v`
Expected: All tests pass
**Step 2: 运行类型检查**
Run: `cd backend && uv run basedpyright src/`
Expected: No errors
**Step 3: 运行 lint**
Run: `cd backend && uv run ruff check src/`
Expected: No errors
---
## Phase 4: 前端适配
### Task 13: 更新 Auth API
**Files:**
- Modify: `apps/lib/features/auth/data/auth_api.dart`
**Step 1: 更新 URL 路径**
```dart
import 'package:social_app/core/api/api_client.dart';
import 'models/signup_request.dart';
import 'models/login_request.dart';
import 'models/auth_response.dart';
class AuthApi {
final ApiClient _client;
static const _prefix = '/api/v1/auth';
AuthApi(this._client);
Future<VerificationCreateResponse> createVerification(SignupStartRequest request) async {
final response = await _client.post(
'$_prefix/verifications',
data: request.toJson(),
);
return VerificationCreateResponse.fromJson(response.data);
}
Future<void> resendVerification(SignupResendRequest request) async {
await _client.post(
'$_prefix/verifications/resend',
data: request.toJson(),
);
}
Future<AuthResponse> verifyVerification(SignupVerifyRequest request) async {
final response = await _client.post(
'$_prefix/verifications/verify',
data: request.toJson(),
);
return AuthResponse.fromJson(response.data);
}
Future<AuthResponse> createSession(LoginRequest request) async {
final response = await _client.post(
'$_prefix/sessions',
data: request.toJson(),
);
return AuthResponse.fromJson(response.data);
}
Future<AuthResponse> refreshSession(RefreshRequest request) async {
final response = await _client.post(
'$_prefix/sessions/refresh',
data: request.toJson(),
);
return AuthResponse.fromJson(response.data);
}
Future<void> deleteSession(LogoutRequest request) async {
await _client.delete(
'$_prefix/sessions',
data: request.toJson(),
);
}
}
```
**Step 2: Commit**
```bash
git add apps/lib/features/auth/data/auth_api.dart
git commit -m "refactor(auth): update API routes to RESTful style"
```
---
### Task 14: 更新 Auth Response Models
**Files:**
- Modify: `apps/lib/features/auth/data/models/auth_response.dart`
**Step 1: 简化响应模型**
```dart
class AuthUser {
final String id;
final String email;
const AuthUser({required this.id, required this.email});
factory AuthUser.fromJson(Map<String, dynamic> json) {
return AuthUser(id: json['id'] as String, email: json['email'] as String);
}
}
class AuthResponse {
final String accessToken;
final String refreshToken;
final int expiresIn;
final String tokenType;
final AuthUser user;
const AuthResponse({
required this.accessToken,
required this.refreshToken,
required this.expiresIn,
required this.tokenType,
required this.user,
});
factory AuthResponse.fromJson(Map<String, dynamic> json) {
return AuthResponse(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
expiresIn: json['expires_in'] as int,
tokenType: json['token_type'] as String,
user: AuthUser.fromJson(json['user'] as Map<String, dynamic>),
);
}
}
class VerificationCreateResponse {
final String email;
const VerificationCreateResponse({required this.email});
factory VerificationCreateResponse.fromJson(Map<String, dynamic> json) {
return VerificationCreateResponse(email: json['email'] as String);
}
}
```
删除 `SignupStartResponse``SignupResendResponse`
**Step 2: Commit**
```bash
git add apps/lib/features/auth/data/models/auth_response.dart
git commit -m "refactor(auth): simplify response models"
```
---
### Task 15: 更新 Auth Repository
**Files:**
- Modify: `apps/lib/features/auth/data/auth_repository.dart`
- Modify: `apps/lib/features/auth/data/auth_repository_impl.dart`
**Step 1: 更新方法名和返回类型**
将方法名和返回类型更新为新的 API 方法。
**Step 2: Commit**
```bash
git add apps/lib/features/auth/data/auth_repository.dart
git add apps/lib/features/auth/data/auth_repository_impl.dart
git commit -m "refactor(auth): update repository for new API"
```
---
### Task 16: 更新 Register Cubit
**Files:**
- Modify: `apps/lib/features/auth/presentation/cubits/register_cubit.dart`
**Step 1: 更新 API 调用**
- `signupStart``createVerification`
- `signupResend``resendVerification`(现在返回 void
- 更新返回类型
**Step 2: Commit**
```bash
git add apps/lib/features/auth/presentation/cubits/register_cubit.dart
git commit -m "refactor(auth): update register cubit for new API"
```
---
### Task 17: 更新 Auth Cubit
**Files:**
- Modify: `apps/lib/features/auth/presentation/cubits/auth_cubit.dart`
**Step 1: 更新 API 调用**
- `login``createSession`
- `refresh``refreshSession`
- `logout``deleteSession`
**Step 2: Commit**
```bash
git add apps/lib/features/auth/presentation/cubits/auth_cubit.dart
git commit -m "refactor(auth): update auth cubit for new API"
```
---
### Task 18: 创建 Users API 和 Repository
**Files:**
- Create: `apps/lib/features/users/data/users_api.dart`
- Create: `apps/lib/features/users/data/users_repository.dart`
- Create: `apps/lib/features/users/data/users_repository_impl.dart`
- Create: `apps/lib/features/users/data/models/user_response.dart`
**Step 1: 创建 users_api.dart**
```dart
import 'package:social_app/core/api/api_client.dart';
import 'models/user_response.dart';
class UsersApi {
final ApiClient _client;
static const _prefix = '/api/v1/users';
UsersApi(this._client);
Future<UserResponse> getMe() async {
final response = await _client.get('$_prefix/me');
return UserResponse.fromJson(response.data);
}
Future<UserResponse> updateMe(UserUpdateRequest request) async {
final response = await _client.patch(
'$_prefix/me',
data: request.toJson(),
);
return UserResponse.fromJson(response.data);
}
Future<UserResponse> getByUsername(String username) async {
final response = await _client.get('$_prefix/$username');
return UserResponse.fromJson(response.data);
}
}
```
**Step 2: 创建 user_response.dart**
```dart
class UserResponse {
final String id;
final String username;
final String? avatarUrl;
final String? bio;
const UserResponse({
required this.id,
required this.username,
this.avatarUrl,
this.bio,
});
factory UserResponse.fromJson(Map<String, dynamic> json) {
return UserResponse(
id: json['id'] as String,
username: json['username'] as String,
avatarUrl: json['avatar_url'] as String?,
bio: json['bio'] as String?,
);
}
}
class UserUpdateRequest {
final String? username;
final String? avatarUrl;
final String? bio;
const UserUpdateRequest({this.username, this.avatarUrl, this.bio});
Map<String, dynamic> toJson() {
return {
if (username != null) 'username': username,
if (avatarUrl != null) 'avatar_url': avatarUrl,
if (bio != null) 'bio': bio,
};
}
}
```
**Step 3: Commit**
```bash
git add apps/lib/features/users/
git commit -m "feat(users): create users API and models"
```
---
### Task 19: 更新使用 Profile 的页面
**Files:**
- Modify: 所有引用 profile API 的 cubit 和 screen
**Step 1: 全局搜索 profile 引用**
Run: `cd apps && grep -r "profile" lib/ --include="*.dart"`
**Step 2: 更新为 users API**
更新所有引用 profile 的代码改为使用新的 users API。
**Step 3: Commit**
```bash
git add apps/lib/
git commit -m "refactor: migrate profile to users API"
```
---
### Task 20: 更新 ApiClient 添加 delete 和 patch 方法
**Files:**
- Modify: `apps/lib/core/api/api_client.dart`
**Step 1: 添加 delete 和 patch 方法**
```dart
Future<Response<T>> delete<T>(
String path, {
dynamic data,
Options? options,
}) async {
try {
return await _dio.delete<T>(path, data: data, options: options);
} on DioException catch (e) {
throw ApiException.fromDioError(e);
}
}
Future<Response<T>> patch<T>(
String path, {
dynamic data,
Options? options,
}) async {
try {
return await _dio.patch<T>(path, data: data, options: options);
} on DioException catch (e) {
throw ApiException.fromDioError(e);
}
}
```
**Step 2: Commit**
```bash
git add apps/lib/core/api/api_client.dart
git commit -m "feat(api): add delete and patch methods"
```
---
### Task 21: 更新前端测试
**Files:**
- Modify: `apps/test/features/auth/presentation/cubits/register_cubit_test.dart`
- 其他相关测试文件
**Step 1: 更新测试用例**
更新 mock 和断言以匹配新的 API 方法名和返回类型。
**Step 2: 运行测试**
Run: `cd apps && flutter test`
Expected: All tests pass
**Step 3: Commit**
```bash
git add apps/test/
git commit -m "test: update tests for RESTful API"
```
---
## Phase 5: 文档
### Task 22: 创建路由文档
**Files:**
- Create: `docs/runtime/runtime-route.md`
**Step 1: 创建路由文档**
```markdown
# Runtime API Routes
本文档记录所有 HTTP API 端点。修改路由时必须同步更新此文档。
## 格式说明
- Request/Response 使用 JSON 格式
- 错误响应使用 RFC 7807 `application/problem+json`
- 所有端点前缀: `/api/v1`
## Auth
### POST /auth/verifications
创建验证码(注册发起)。
**Request:**
```json
{
"username": "string (3-30 chars)",
"email": "string (email)",
"password": "string (min 6 chars)",
"redirect_to": "string? (optional)"
}
```
**Response:** 202 Accepted
```json
{
"email": "user@example.com"
}
```
**Errors:**
- 422: 请求参数无效
- 429: 请求过于频繁
---
### POST /auth/verifications/resend
重发验证码。
**Request:**
```json
{
"email": "string (email)"
}
```
**Response:** 204 No Content
**Errors:**
- 422: 请求参数无效
- 429: 请求过于频繁
---
### POST /auth/verifications/verify
验证码校验。
**Request:**
```json
{
"email": "string (email)",
"token": "string (6 digits)"
}
```
**Response:** 200 OK
```json
{
"access_token": "string",
"refresh_token": "string",
"expires_in": 3600,
"token_type": "bearer",
"user": {
"id": "string",
"email": "string"
}
}
```
**Errors:**
- 401: 验证码无效或已过期
- 422: 请求参数无效
- 429: 请求过于频繁
---
### POST /auth/sessions
登录(创建会话)。
**Request:**
```json
{
"email": "string (email)",
"password": "string (min 6 chars)"
}
```
**Response:** 200 OK
```json
{
"access_token": "string",
"refresh_token": "string",
"expires_in": 3600,
"token_type": "bearer",
"user": {
"id": "string",
"email": "string"
}
}
```
**Errors:**
- 401: 邮箱或密码错误
- 422: 请求参数无效
- 429: 请求过于频繁
---
### POST /auth/sessions/refresh
刷新 Token。
**Request:**
```json
{
"refresh_token": "string"
}
```
**Response:** 200 OK
```json
{
"access_token": "string",
"refresh_token": "string",
"expires_in": 3600,
"token_type": "bearer",
"user": {
"id": "string",
"email": "string"
}
}
```
**Errors:**
- 401: 无效的 refresh token
- 422: 请求参数无效
---
### DELETE /auth/sessions
登出(删除会话)。
**Request:**
```json
{
"refresh_token": "string"
}
```
**Response:** 204 No Content
**Errors:**
- 422: 请求参数无效
---
### GET /auth/users
按邮箱查询用户(需要认证)。
**Query Parameters:**
- `email`: string (required)
**Response:** 200 OK
```json
{
"id": "string",
"email": "string",
"created_at": "string (ISO 8601)",
"email_confirmed_at": "string? (ISO 8601)"
}
```
**Errors:**
- 403: 无权限访问
- 404: 用户不存在
- 422: 请求参数无效
---
## Users
### GET /users/me
获取当前用户信息(需要认证)。
**Response:** 200 OK
```json
{
"id": "string",
"username": "string",
"avatar_url": "string?",
"bio": "string?"
}
```
**Errors:**
- 401: 未认证
---
### PATCH /users/me
更新当前用户信息(需要认证)。
**Request:**
```json
{
"username": "string? (3-30 chars)",
"avatar_url": "string? (URL)",
"bio": "string? (max 200 chars)"
}
```
**Response:** 200 OK
```json
{
"id": "string",
"username": "string",
"avatar_url": "string?",
"bio": "string?"
}
```
**Errors:**
- 401: 未认证
- 422: 请求参数无效
---
### GET /users/{username}
按用户名查询用户(需要认证)。
**Path Parameters:**
- `username`: string (3-30 chars, alphanumeric and underscore)
**Response:** 200 OK
```json
{
"id": "string",
"username": "string",
"avatar_url": "string?",
"bio": "string?"
}
```
**Errors:**
- 401: 未认证
- 404: 用户不存在
- 422: 请求参数无效
---
## Agent Chat
### POST /agent-chats
运行 Agent 对话(需要认证)。
**Request:**
```json
{
"message": "string (1-8000 chars)",
"session_id": "string? (UUID)"
}
```
**Response:** 200 OK
```json
{
"session_id": "string (UUID)",
"output": "string",
"events": [
{
"type": "string",
"run_id": "string?",
"message_id": "string?",
"delta": "string?",
"tool_name": "string?",
"result": "string?",
"output": "string?",
"error": "string?"
}
]
}
```
**Errors:**
- 401: 未认证
- 422: 请求参数无效
---
## Infra
### GET /infra/health
检查基础设施健康状态。
**Response:** 200 OK
```json
{
"status": "healthy" | "unhealthy",
"services": {
"redis": {
"status": "healthy" | "unhealthy",
"latency_ms": 0
}
}
}
```
---
### GET /health
检查服务健康状态。
**Response:** 200 OK
```json
{
"status": "ok"
}
```
---
## Error Response Format (RFC 7807)
所有错误响应使用 `application/problem+json` 格式:
```json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "验证码无效或已过期",
"instance": "/api/v1/auth/verifications/verify"
}
```
前端应优先读取 `detail` 字段显示给用户。
```
**Step 2: Commit**
```bash
git add docs/runtime/runtime-route.md
git commit -m "docs: add runtime route documentation"
```
---
### Task 23: 更新根目录 AGENTS.md
**Files:**
- Modify: `AGENTS.md`
**Step 1: 添加路由同步规则**
在文件末尾添加:
```markdown
## API Route Documentation
When modifying HTTP routes (adding, updating, or removing endpoints):
- Sync changes to `docs/runtime/runtime-route.md`
- Include: HTTP method, path, request/response schema, status codes, error format
- Keep documentation in sync with actual implementation
```
**Step 2: Commit**
```bash
git add AGENTS.md
git commit -m "docs: add route sync rule to AGENTS.md"
```
---
## Phase 6: 验收
### Task 24: 端到端验证
**Step 1: 启动后端服务**
Run: `docker compose --env-file .env -f infra/docker/docker-compose.yml up -d`
**Step 2: 运行后端测试**
Run: `cd backend && uv run pytest -v`
Expected: All tests pass
**Step 3: 运行前端测试**
Run: `cd apps && flutter test`
Expected: All tests pass
**Step 4: 手动验证注册/登录流程**
使用 Flutter app 或 curl 测试:
1. POST /api/v1/auth/verifications - 注册
2. POST /api/v1/auth/verifications/verify - 验证
3. POST /api/v1/auth/sessions - 登录
4. GET /api/v1/users/me - 获取用户信息
5. DELETE /api/v1/auth/sessions - 登出
---
## Summary
| Phase | Tasks | Description |
|-------|-------|-------------|
| Phase 1 | 1-3 | 后端 Schema 重构 |
| Phase 2 | 4-9 | 后端路由重构 |
| Phase 3 | 10-12 | 后端测试更新 |
| Phase 4 | 13-21 | 前端适配 |
| Phase 5 | 22-23 | 文档 |
| Phase 6 | 24 | 验收 |