refactor: 优化日历状态管理与首页输入框,添加API客户端抽象

This commit is contained in:
qzl
2026-02-27 18:36:21 +08:00
parent 80d04688fc
commit 3d6ae7695f
20 changed files with 2146 additions and 801 deletions
@@ -1,204 +0,0 @@
# Auth UX Enhancement Design
**日期**: 2026-02-26
**状态**: 可实施(修订版)
## 目标
本次改动聚焦 4 个问题:
1. 注册验证码页增加首次提示,降低用户困惑。
2. 增加忘记密码流程(验证码模式)。
3. 注册页增加邀请码输入(前端收集,后端暂不消费)。
4. 修复用户名非唯一导致的用户查询问题,改为搜索接口。
## 非目标
- 不实现邀请码校验/入库。
- 不改动 Supabase 邮件模板基础设施(当前 self-hosted 已配置 recovery 模板 URL)。
- 不在本次引入新的认证机制(仅沿用 Supabase OTP + session)。
---
## 1. 忘记密码的可落地后端方案(对外两步)
### 1.1 约束说明(关键)
当前 Python SDK 的 `verify_otp` 参数模型不支持在验码时直接携带 `new_password`。因此不能走“单接口验码并改密”的实现。
### 1.2 可执行流程
对客户端暴露两步流程,第二步在后端内部执行两段动作:
1. `POST /auth/password-reset`:调用 Supabase `reset_password_email` 发送 recovery 验证码。
2. `POST /auth/password-reset/confirm`:接收 `email + token + new_password`,后端内部先调用 `verify_otp(type="recovery")`,再基于该会话调用 `update_user(password=...)`
这样既匹配 SDK 能力(`verify_otp` 不支持直接带 `new_password`),又保持前端体验为两步。
### 1.3 API 设计
#### POST /auth/password-reset
发送重置验证码。
Request
```json
{
"email": "string(email)",
"redirect_to": "string(optional)"
}
```
Response: `204 No Content`
Errors:
- `422` 参数错误
- `429` 频率受限
#### POST /auth/password-reset/confirm
验证 recovery 验证码并完成改密。
Request
```json
{
"email": "string(email)",
"token": "string(6 digits)",
"new_password": "string(min 6)"
}
```
Response: `204 No Content`
Errors:
- `401` 验证码无效或过期
- `422` 参数错误
- `429` 频率受限
### 1.4 安全边界
- 用户档案更新走 `users` 域(`/users/me` -> `UserService` -> `Profile`),仅允许公开资料字段。
- 密码修改走 `auth` 域(Supabase Auth),不复用 `users` service/repository。
- `POST /auth/password-reset/confirm` 必须在同一请求内完成“验码 + 改密”,禁止单独暴露“仅改密”接口。
- 即使伪造 `/users/me` 请求,也无法触发密码修改路径。
---
## 2. 前端忘记密码流程
流程:
`登录页` -> `忘记密码页(输入邮箱)` -> `验证码+新密码页` -> `返回登录并用新密码登录`
关键点:
- 第二步页面一次提交 `email + token + new_password``/auth/password-reset/confirm`
- 所有用户反馈统一使用 `Toast`(遵循 `apps/AGENTS.md`)。
- 错误提示优先展示后端 `detail`
---
## 3. 注册 UX 优化
### 3.1 验证码发送提示
`register_verification_screen.dart` 首次进入页面显示:
`验证码已发送,如未收到请检查垃圾邮件或确认邮箱已注册`
### 3.2 邀请码输入
在注册页新增可选字段:
- Label: `邀请码(选填)`
- Hint: `请输入邀请码`
前端请求体可携带 `invite_code`,后端忽略该字段,不返回错误。
---
## 4. 用户搜索 Bug 修复
### 4.1 问题
`GET /users/{username}` 隐含“用户名唯一”假设,实际不成立。
### 4.2 方案
后端删除 `GET /users/{username}`,改为 `POST /users/search`
Request
```json
{
"query": "string(1-100)"
}
```
Response
```json
[
{
"id": "string",
"username": "string",
"avatar_url": "string|null",
"bio": "string|null"
}
]
```
查询策略:
- username: 模糊匹配(`ilike`
- email: 精确匹配
- 最多返回 20 条
- 返回公开字段,不返回 email
### 4.3 前端联动
必须同步迁移 `apps/lib/features/users/data/*``getByUsername` -> `searchUsers`),否则删除后端旧路由后前端会直接 404。
---
## 5. 主要改动文件
### 后端
- `backend/src/v1/auth/schemas.py`
- `backend/src/v1/auth/service.py`
- `backend/src/v1/auth/gateway.py`
- `backend/src/v1/auth/router.py`
- `backend/src/v1/users/schemas.py`
- `backend/src/v1/users/repository.py`
- `backend/src/v1/users/service.py`
- `backend/src/v1/users/router.py`
- `backend/tests/integration/test_auth_routes.py`
- `backend/tests/integration/test_users_routes.py`
### 前端
- `apps/lib/features/auth/ui/screens/login_screen.dart`
- `apps/lib/features/auth/ui/screens/register_screen.dart`
- `apps/lib/features/auth/ui/screens/register_verification_screen.dart`
- `apps/lib/features/auth/ui/screens/forgot_password_screen.dart`(新增)
- `apps/lib/features/auth/ui/screens/reset_password_screen.dart`(新增)
- `apps/lib/features/auth/presentation/cubits/forgot_password_cubit.dart`(新增)
- `apps/lib/features/auth/presentation/cubits/reset_password_cubit.dart`(新增)
- `apps/lib/features/auth/data/auth_api.dart`
- `apps/lib/features/auth/data/auth_repository.dart`
- `apps/lib/features/auth/data/auth_repository_impl.dart`
- `apps/lib/features/users/data/users_api.dart`
- `apps/lib/features/users/data/users_repository.dart`
- `apps/lib/features/users/data/users_repository_impl.dart`
- `apps/lib/core/router/app_router.dart`
### 文档
- `docs/runtime/runtime-route.md`(按 AGENTS 规则必须同步)
---
## 6. 验收标准
- [ ] 注册验证码页首次进入显示提示。
- [ ] 登录页出现“忘记密码”入口。
- [ ] 忘记密码流程可完整走通(发码、确认改密、重新登录)。
- [ ] 注册页可输入邀请码且不影响注册。
- [ ] `GET /users/{username}` 被移除。
- [ ] `POST /users/search` 可用且返回不含 email。
- [ ] 后端与前端相关测试通过,文档已同步。
@@ -1,402 +0,0 @@
# Auth UX Enhancement Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 以最小风险完成 Auth UX 优化(忘记密码、注册提示、邀请码、用户搜索修复),并确保与现有 Supabase SDK 能力完全一致。
**Architecture:** 对客户端暴露两步重置流程:`password-reset` 发码 + `password-reset/confirm` 确认改密。`confirm` 在后端内部串行执行 `verify_otp(type="recovery")``update_user(password=...)`,既符合 SDK 限制又保持交互简洁。用户查询从 `GET /users/{username}` 迁移到 `POST /users/search`,并同步前端数据层,避免断链。全流程按 TDD 执行并同步路由文档。
**Tech Stack:** FastAPI, Pydantic, SQLAlchemy, Supabase Auth, Flutter, Dio, Bloc/Cubit
---
## Phase 0: 基线与保护
### Task 1: 建立基线测试(RED 前准备)
**Files:**
- Modify: `backend/tests/integration/test_auth_routes.py`
- Modify: `backend/tests/integration/test_users_routes.py`
**Step 1: 新增失败测试占位(不改实现)**
新增并期望失败的测试:
- `test_password_reset_request_returns_204`
- `test_password_reset_confirm_returns_204`
- `test_search_users_returns_list`
**Step 2: 运行后端测试确认 RED**
Run: `uv run pytest backend/tests/integration/test_auth_routes.py backend/tests/integration/test_users_routes.py -v`
Expected: 新增用例失败(404/AttributeError/未实现)。
**Step 3: Commit**
```bash
git add backend/tests/integration/test_auth_routes.py backend/tests/integration/test_users_routes.py
git commit -m "test: add failing tests for auth ux enhancement"
```
---
## Phase 1: 后端密码重置(对外两步)
### Task 2: 完成密码重置请求接口(发码)
**Files:**
- Modify: `backend/src/v1/auth/schemas.py`
- Modify: `backend/src/v1/auth/service.py`
- Modify: `backend/src/v1/auth/gateway.py`
- Modify: `backend/src/v1/auth/router.py`
- Test: `backend/tests/integration/test_auth_routes.py`
**Step 1: 写失败测试(若 Task 1 未覆盖细节)**
确保 `POST /api/v1/auth/password-reset`
- 合法邮箱返回 `204`
- 非法参数返回 `422`
- 触发限流返回 `429`
**Step 2: 跑单测确认失败**
Run: `uv run pytest backend/tests/integration/test_auth_routes.py::test_password_reset_request_returns_204 -v`
Expected: FAIL。
**Step 3: 最小实现(GREEN**
- `schemas.py`: 复用已有 `PasswordResetRequest`,不要重复定义同名模型。
- `service.py`: 增加 `request_password_reset(...)`
- `gateway.py`: 调用 `self._client.auth.reset_password_email(email, options)`
- `router.py`: 新增 `POST /auth/password-reset`,返回 `204`,保留限流。
**Step 4: 跑测试确认通过**
Run: `uv run pytest backend/tests/integration/test_auth_routes.py::test_password_reset_request_returns_204 -v`
Expected: PASS。
**Step 5: Commit**
```bash
git add backend/src/v1/auth/schemas.py backend/src/v1/auth/service.py backend/src/v1/auth/gateway.py backend/src/v1/auth/router.py backend/tests/integration/test_auth_routes.py
git commit -m "feat(auth): add password reset request endpoint"
```
---
### Task 3: 完成密码重置确认接口(验码 + 改密)
**Files:**
- Modify: `backend/src/v1/auth/schemas.py`
- Modify: `backend/src/v1/auth/service.py`
- Modify: `backend/src/v1/auth/gateway.py`
- Modify: `backend/src/v1/auth/router.py`
- Test: `backend/tests/integration/test_auth_routes.py`
**Step 1: 写失败测试**
测试 `POST /api/v1/auth/password-reset/confirm`
- 正确 `email + token + new_password` 返回 `204`
- 错误验证码返回 `401`
- 弱密码/参数错误返回 `422`
**Step 2: 跑测试确认失败**
Run: `uv run pytest backend/tests/integration/test_auth_routes.py::test_password_reset_confirm_returns_204 -v`
Expected: FAIL。
**Step 3: 最小实现**
- `schemas.py`: 新增 `PasswordResetConfirmRequest(email, token, new_password)`
- `service.py`: 增加 `confirm_password_reset(...)`
- `gateway.py`: 在单个方法内先调用 `verify_otp({"type":"recovery", ...})`,再在该会话上下文调用 `update_user({"password": new_password})`
- `router.py`: 新增 `POST /auth/password-reset/confirm`,返回 `204`
**Step 4: 跑测试确认通过**
Run: `uv run pytest backend/tests/integration/test_auth_routes.py::test_password_reset_confirm_returns_204 -v`
Expected: PASS。
**Step 5: Commit**
```bash
git add backend/src/v1/auth/schemas.py backend/src/v1/auth/service.py backend/src/v1/auth/gateway.py backend/src/v1/auth/router.py backend/tests/integration/test_auth_routes.py
git commit -m "feat(auth): add password reset confirm endpoint"
```
---
## Phase 2: 后端用户搜索替代用户名路由
### Task 4: 新增 POST /users/search 并移除旧路由
**Files:**
- Modify: `backend/src/v1/users/schemas.py`
- Modify: `backend/src/v1/users/repository.py`
- Modify: `backend/src/v1/users/service.py`
- Modify: `backend/src/v1/users/router.py`
- Test: `backend/tests/integration/test_users_routes.py`
**Step 1: 写失败测试**
新增测试:
- `POST /api/v1/users/search` 成功返回列表
- `query` 为空返回 `422`
- 删除后 `GET /api/v1/users/{username}` 返回 `404`
**Step 2: 跑测试确认失败**
Run: `uv run pytest backend/tests/integration/test_users_routes.py -v`
Expected: FAIL。
**Step 3: 最小实现**
- `schemas.py`: 增加 `UserSearchRequest`, `UserSearchResult`
- `repository.py`: 新增 `search_users(query)`username `ilike` + email 精确匹配,`limit 20`
- `service.py`: 增加 `search_users(...)` 并映射公开字段。
- `router.py`: 增加 `POST /users/search`;删除 `GET /users/{username}`
**Step 4: 跑测试确认通过**
Run: `uv run pytest backend/tests/integration/test_users_routes.py -v`
Expected: PASS。
**Step 5: Commit**
```bash
git add backend/src/v1/users/schemas.py backend/src/v1/users/repository.py backend/src/v1/users/service.py backend/src/v1/users/router.py backend/tests/integration/test_users_routes.py
git commit -m "refactor(users): replace username endpoint with search"
```
---
## Phase 3: 前端认证 UX
### Task 5: 数据层支持密码重置两步接口
**Files:**
- Modify: `apps/lib/features/auth/data/models/login_request.dart`
- Modify: `apps/lib/features/auth/data/auth_api.dart`
- Modify: `apps/lib/features/auth/data/auth_repository.dart`
- Modify: `apps/lib/features/auth/data/auth_repository_impl.dart`
- Test: `apps/test/features/auth/data/auth_repository_impl_test.dart`(如不存在则创建)
**Step 1: 写失败测试**
覆盖:
- request -> confirm 的调用顺序
- confirm 请求包含 `email + token + new_password`
**Step 2: 跑测试确认失败**
Run: `flutter test apps/test/features/auth/data/auth_repository_impl_test.dart`
Expected: FAIL。
**Step 3: 最小实现**
- 增加 `requestPasswordReset` / `confirmPasswordReset`
- 模型保持 snake_case JSON 键与后端一致。
**Step 4: 跑测试确认通过**
Run: `flutter test apps/test/features/auth/data/auth_repository_impl_test.dart`
Expected: PASS。
**Step 5: Commit**
```bash
git add apps/lib/features/auth/data apps/test/features/auth/data/auth_repository_impl_test.dart
git commit -m "feat(auth): add password reset data layer"
```
---
### Task 6: 新增忘记密码页面与状态管理
**Files:**
- Create: `apps/lib/features/auth/ui/screens/forgot_password_screen.dart`
- Create: `apps/lib/features/auth/ui/screens/reset_password_screen.dart`
- Create: `apps/lib/features/auth/presentation/cubits/forgot_password_cubit.dart`
- Create: `apps/lib/features/auth/presentation/cubits/reset_password_cubit.dart`
- Modify: `apps/lib/features/auth/ui/screens/login_screen.dart`
- Modify: `apps/lib/core/router/app_router.dart`
- Test: `apps/test/features/auth/ui/forgot_password_screen_test.dart`(可新建)
**Step 1: 写失败测试**
至少覆盖:
- 登录页点击“忘记密码”可跳转
- 忘记密码页提交后进入验证码改密页
**Step 2: 跑测试确认失败**
Run: `flutter test apps/test/features/auth/ui/forgot_password_screen_test.dart`
Expected: FAIL。
**Step 3: 最小实现**
- 使用 `Toast` 呈现提交成功/失败反馈。
- reset 页面提交时调用单个 confirm 接口完成验码与改密。
- 成功后跳回登录并提示“密码已重置”。
**Step 4: 跑测试确认通过**
Run: `flutter test apps/test/features/auth/ui/forgot_password_screen_test.dart`
Expected: PASS。
**Step 5: Commit**
```bash
git add apps/lib/features/auth apps/lib/core/router/app_router.dart apps/test/features/auth/ui/forgot_password_screen_test.dart
git commit -m "feat(auth): add forgot password ui flow"
```
---
### Task 7: 注册体验优化(提示 + 邀请码)
**Files:**
- Modify: `apps/lib/features/auth/ui/screens/register_verification_screen.dart`
- Modify: `apps/lib/features/auth/ui/screens/register_screen.dart`
- Modify: `apps/lib/features/auth/presentation/cubits/register_cubit.dart`
- Modify: `apps/lib/features/auth/data/models/signup_request.dart`
- Test: `apps/test/features/auth/ui/register_screen_test.dart`(如不存在则创建)
**Step 1: 写失败测试**
覆盖:
- 验证码页首次进入显示提示 Toast
- 注册页存在邀请码输入并为可选
**Step 2: 跑测试确认失败**
Run: `flutter test apps/test/features/auth/ui/register_screen_test.dart`
Expected: FAIL。
**Step 3: 最小实现**
- `signup_request` 可选字段 `invite_code`
- 页面展示邀请码输入框,不做必填校验。
**Step 4: 跑测试确认通过**
Run: `flutter test apps/test/features/auth/ui/register_screen_test.dart`
Expected: PASS。
**Step 5: Commit**
```bash
git add apps/lib/features/auth/ui/screens/register_verification_screen.dart apps/lib/features/auth/ui/screens/register_screen.dart apps/lib/features/auth/presentation/cubits/register_cubit.dart apps/lib/features/auth/data/models/signup_request.dart apps/test/features/auth/ui/register_screen_test.dart
git commit -m "feat(auth): improve verification hint and invite code input"
```
---
## Phase 4: 前端 users 数据层迁移
### Task 8: 将 getByUsername 迁移为 searchUsers
**Files:**
- Modify: `apps/lib/features/users/data/users_api.dart`
- Modify: `apps/lib/features/users/data/users_repository.dart`
- Modify: `apps/lib/features/users/data/users_repository_impl.dart`
- Test: `apps/test/features/users/data/users_repository_test.dart`(如不存在则创建)
**Step 1: 写失败测试**
覆盖:
- `searchUsers(query)` 发送 `POST /api/v1/users/search`
- 返回列表模型映射正确
**Step 2: 跑测试确认失败**
Run: `flutter test apps/test/features/users/data/users_repository_test.dart`
Expected: FAIL。
**Step 3: 最小实现**
- 删除 `getByUsername`
- 新增 `searchUsers(String query)`
**Step 4: 跑测试确认通过**
Run: `flutter test apps/test/features/users/data/users_repository_test.dart`
Expected: PASS。
**Step 5: Commit**
```bash
git add apps/lib/features/users/data apps/test/features/users/data/users_repository_test.dart
git commit -m "refactor(users): migrate client to search endpoint"
```
---
## Phase 5: 文档与全量验证
### Task 9: 更新 API 文档并完成全量检查
**Files:**
- Modify: `docs/runtime/runtime-route.md`
**Step 1: 更新路由文档**
- 新增:
- `POST /auth/password-reset`
- `POST /auth/password-reset/confirm`
- `POST /users/search`
- 删除:
- `GET /users/{username}`
文档需包含:请求/响应 schema、状态码、错误格式(RFC 7807)。
**Step 2: 跑后端验证**
Run: `uv run pytest backend/tests -v && uv run basedpyright backend/src`
Expected: 全通过。
**Step 3: 跑前端验证**
Run: `flutter analyze apps/lib && flutter test`
Expected: 全通过。
**Step 4: 手动验收**
- 忘记密码完整链路(发码/确认改密/登录)
- 注册页邀请码与验证码提示
- `POST /users/search` 返回结果正确
**Step 5: Commit**
```bash
git add docs/runtime/runtime-route.md
git commit -m "docs: sync runtime routes for auth ux enhancement"
```
---
## 验收清单
- [ ] 所有新增测试先失败后通过(有 RED/GREEN 记录)
- [ ] 后端密码重置两步接口可用(confirm 内部完成验码 + 改密)
- [ ] 前端忘记密码流程可用
- [ ] 邀请码输入为选填且不破坏现有注册
- [ ] `GET /users/{username}` 全链路移除
- [ ] `POST /users/search` 前后端一致
- [ ] `docs/runtime/runtime-route.md` 已同步
@@ -0,0 +1,191 @@
# Design: Schedule Items API
**Date:** 2026-02-27
**Status:** Approved
## Overview
实现日历事项(Schedule Items)的后端 CRUD API,支持用户创建、查询、更新、删除日历事项。
## Scope
- 仅后端 API,不涉及前端
- 全量 CRUD
- 查询按时间范围筛选
- 暂不支持重复日程(recurrence_rule 留空)
## API Endpoints
### 1. 创建日历事项
```
POST /api/v1/schedule-items
```
**Request:**
```json
{
"title": "string (1-255 chars, required)",
"description": "string? (max 2000 chars)",
"start_at": "string (ISO 8601 datetime, required)",
"end_at": "string? (ISO 8601 datetime, must be after start_at)",
"timezone": "string? (default: UTC)",
"metadata": {
"color": "#FF6B6B",
"location": "会议室A",
"notes": "记得带身份证",
"attachments": [],
"version": 1
}
}
```
**Response:** 201 Created
```json
{
"id": "uuid",
"title": "string",
"description": "string?",
"start_at": "string",
"end_at": "string?",
"timezone": "string",
"metadata": {...},
"status": "active",
"source_type": "manual",
"created_at": "string",
"updated_at": "string"
}
```
### 2. 查询日历事项列表
```
GET /api/v1/schedule-items?start_at=2026-02-01&end_at=2026-02-28
```
**Query Parameters:**
- `start_at`: ISO 8601 date/datetime(查询范围起始)
- `end_at`: ISO 8601 date/datetime(查询范围结束)
**Response:** 200 OK
```json
[
{
"id": "uuid",
"title": "string",
"start_at": "string",
"end_at": "string?",
"timezone": "string",
"status": "active"
}
]
```
### 3. 获取单个事项
```
GET /api/v1/schedule-items/{id}
```
**Response:** 200 OK(完整字段,同创建响应)
### 4. 更新事项
```
PATCH /api/v1/schedule-items/{id}
```
**Request:** 支持 `title`/`description`/`start_at`/`end_at`/`timezone`/`metadata`/`status` 部分更新
**Response:** 200 OK
### 5. 删除事项
```
DELETE /api/v1/schedule-items/{id}
```
**Response:** 204 No Content(软删除)
## Data Models
### Metadata 结构(Pydantic
```python
from enum import Enum
from pydantic import BaseModel
from uuid import UUID
class AttachmentType(str, Enum):
DOCUMENT = "document"
REMINDER = "reminder"
class ScheduleItemMetadataAttachment(BaseModel):
name: str
type: AttachmentType
visible_to: list[UUID] = []
# document 类型
url: str | None = None
note: str | None = None
# reminder 类型
content: str | None = None
class ScheduleItemMetadata(BaseModel):
color: str | None = None
location: str | None = None
notes: str | None = None
attachments: list[ScheduleItemMetadataAttachment] = []
version: int = 1
```
### 数据库模型(已有)
参见 `backend/src/models/schedule_items.py`
- `id`: UUID
- `owner_id`: UUID
- `title`: String(255)
- `description`: Text
- `start_at`: DateTime(timezone=True)
- `end_at`: DateTime(timezone=True)
- `timezone`: String(50)
- `extra_metadata`: JSONB (mapped as "metadata")
- `recurrence_rule`: String(255)
- `source_type`: Enum (MANUAL/IMPORTED/AGENT_GENERATED)
- `status`: Enum (ACTIVE/COMPLETED/CANCELED/ARCHIVED)
- `created_by`: UUID
## Architecture
遵循项目 `schemas / repository / service / router` 分层模式:
```
backend/src/v1/schedule_items/
├── __init__.py
├── schemas.py # Pydantic 请求/响应模型
├── repository.py # CRUD 操作(无 auth,无 commit
├── service.py # 业务逻辑 + 授权 + 事务边界
├── router.py # FastAPI 路由定义
└── dependencies.py # DI(如有)
```
## Security
- 所有端点需要认证(JWT
- `owner_id` 从 JWT `sub` 提取,不从请求体读取
- 用户只能操作自己的日历事项(`owner_id` 过滤)
- RLS 已在数据库层启用(防御边界)
## Error Handling
使用 RFC 7807 `application/problem+json` 格式:
- 400: 请求参数无效
- 401: 未认证
- 404: 事项不存在或无权限访问
- 422: 验证失败
## Out of Scope
- 重复日程(recurrence_rule
- 日程订阅与协作(schedule_subscriptions
- 待办事项联动(todos/todo_sources
- 前端实现
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
# Frontend Runtime Runbook
**Date:** 2026-02-27
**Status:** Active
**Audience:** 前端开发
---
## 开发环境
### Mock 模式
前端开发时可通过 `--dart-define` 切换 Mock 模式,无需后端即可运行:
```bash
# Mock 模式(本地开发,无需后端)
flutter run --dart-define=MOCK_API=true
# 正式模式(需要后端运行)
flutter run
```
### Mock 自动登录
Mock 模式下,启动 App 时会自动使用测试账号登录并跳转到首页。
**测试账号(Mock:**
| 场景 | 邮箱 | 密码 | 说明 |
|------|------|------|------|
| 正常登录 | 任意非 error@test.com | 任意 | 登录成功 |
| 登录失败 | error@test.com | 任意 | 返回 401 |
**验证码:** 任意 6 位数字(建议使用 `123456`
---
## 打包构建
### Debug Build
```bash
# Mock 模式
flutter build apk --debug --dart-define=MOCK_API=true
# 正式模式
flutter build apk --debug
```
### Release Build
Release 构建强制使用正式 API,不受 `MOCK_API` 影响:
```bash
flutter build apk --release
```
---
## 调试运行
### 命令行调试
```bash
# Mock 模式(无需后端,自动登录)
flutter run --dart-define=MOCK_API=true -d emulator-5554
# 正式模式(需要后端运行)
flutter run -d emulator-5554
```
### VSCode 调试配置
`.vscode/launch.json` 中添加配置:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Mock Mode",
"request": "launch",
"type": "dart",
"args": ["--dart-define=MOCK_API=true"]
},
{
"name": "正式模式",
"request": "launch",
"type": "dart"
}
]
}
```
配置完成后,在 VSCode 左侧 Debug 面板的 dropdown 中选择 "Mock Mode" 或 "正式模式" 进行调试。
---
## Change Log
| 日期 | 变更 |
|------|------|
| 2026-02-27 | 新增 Frontend Runbook,支持 --dart-define=MOCK_API=true 切换 Mock 模式 |