# 缺失项与待补全方向

> 基于 v0.1 创业计划文档与当前 iOS 代码对比分析
> 整理时间：2026-05-10

本文档系统性列出知习 iOS App 当前在架构、页面、功能、设计等方面的缺失项，并给出优先级建议。

---

## 一、架构层缺失

### 1.1 MVVM 分层

**现状**：全部代码写在 SwiftUI View 中，无任何 ViewModel/ObservableObject/@Published。grep 搜索 ViewModel、ObservableObject、@Published 均为零结果。

**缺失**：
- 无 ViewModel 层，业务逻辑、状态管理、数据转换全部堆在 View 里
- 无 Model 层，数据结构通过 View 内的局部 struct 或硬编码数据隐式定义
- 代码不可测试，无法单独验证业务逻辑

**计划要求**（`官网与技术基础.md` 第 5.3 节）：
```
AIStudyApp/
├── Features/
│   ├── Onboarding/
│   │   ├── Views/       ← 当前有，但无 ViewModel/Model 子目录
│   │   ├── ViewModels/  ← 缺失
│   │   └── Models/      ← 缺失
```

### 1.2 Service 层

**现状**：无任何 Service 类，AI 分析、学习记录、用户管理等概念没有对应的服务抽象。

**缺失**：
| Service | 职责 | 设计文档 |
|---------|------|----------|
| AuthService | Apple 登录、Token 管理、会话维护 | `docs/AI对话.md`（详细设计） |
| LearningService | 学习记录 CRUD、进度追踪 | — |
| AIService | AI 分析请求代理、结果解析 | — |
| ReviewService | 复习任务生成、调度 | — |
| KnowledgeService | 知识库/路径/课程查询 | — |
| FeedbackService | 用户反馈提交 | — |

**Auth 模块已有详细文件清单**（来自 `docs/AI对话.md`）：
```
Features/Auth/Views/LoginView.swift
Features/Auth/ViewModels/LoginViewModel.swift

Core/Services/AuthService.swift
Core/Services/AuthServiceProtocol.swift

Core/Storage/KeychainStore.swift
Core/Storage/TokenStore.swift

App/AppSession.swift

Core/Models/AuthModels.swift
Core/Models/User.swift
```

### 1.3 Repository 层

**现状**：零数据持久化，所有"数据"均为 View 中硬编码的 mock。

**缺失**：
- 无数据访问抽象（未来可能切换 CoreData → API，需要 Repository 隔离）
- 无本地缓存层
- 无网络数据源层

### 1.4 网络层

**现状**：无任何网络请求代码，无 APIClient，无 URLSession 调用。

**缺失**：
- APIClient（封装 URLSession，注入 baseURL、header、token）
- APIEndpoint（枚举化 API 路径，统一请求构建）
- APIError（统一错误模型和处理）
- 请求/响应拦截器（日志、token 刷新）
- Mock 层（本地开发和 UI 预览用）

### 1.5 依赖注入

**现状**：无任何 DI 模式，Service 和 ViewModel 尚未创建，暂时不存在注入问题。但需要在架构搭建时建立模式。

**建议**：初期使用构造函数注入 + `@EnvironmentObject`，避免引入第三方 DI 框架。

---

## 二、核心能力缺失

### 2.1 Sign in with Apple（已有详细设计文档）

**现状**：`LoginPage` 有 UI（手机号/邮箱/微信/Apple 入口），但 `AIStudyAppApp` 仅用 `@AppStorage("hasCompletedOnboarding")` 控制是否进入主界面，无实际认证。

**计划要求**：第一版登录方式仅为 Sign in with Apple（`Demo与MVP.md` 第 5.2 节）。

**详细设计方案**见 `docs/AI对话.md`，核心结论：

**登录页只保留一个入口**：
```
Sign in with Apple
```

**删除的入口**：手机号登录、邮箱登录、微信登录、验证码登录

**"跳过"按钮处理**：
```swift
#if DEBUG
Button("跳过，进入演示模式") { ... }
#endif
```
正式环境不展示跳过入口，避免后续匿名用户迁移问题。

**登录流程设计**：
```
App 启动
    ↓
AppSession 检查 Keychain 是否有 refreshToken
    ↓
有 token → 调用 /auth/refresh 或 /users/me
    ↓
成功 → 进入主界面
失败 → 清空 token，进入登录页
    ↓
无 token → 进入登录页
    ↓
用户点击 Sign in with Apple
    ↓
获取 identityToken / authorizationCode / userIdentifier
    ↓
POST /api/auth/apple
    ↓
后端返回 accessToken / refreshToken / user
    ↓
token 存入 Keychain（不要 UserDefaults）
    ↓
判断 user.onboardingCompleted
    ↓
未完成 → 引导/目标设置
已完成 → 主界面
```

**登录相关新增文件**：
```
Features/Auth/Views/LoginView.swift          # 仅 Apple 登录按钮
Features/Auth/ViewModels/LoginViewModel.swift # @MainActor, @Published isLoading/errorMessage

Core/Services/AuthService.swift
Core/Services/AuthServiceProtocol.swift

Core/Storage/KeychainStore.swift
Core/Storage/TokenStore.swift

App/AppSession.swift                         # 全局登录状态管理

Core/Models/AuthModels.swift                 # AppleLoginRequest, AuthResponse
Core/Models/User.swift
```

**API Contract（Auth）**：
```swift
// 请求
struct AppleLoginRequest: Encodable {
    let identityToken: String
    let authorizationCode: String?
    let userIdentifier: String
    let fullName: AppleFullName?
    let email: String?
}

// 响应
struct AuthResponse: Decodable {
    let accessToken: String
    let refreshToken: String
    let expiresIn: Int
    let user: User
}
```

**关键约束**：
- 不再用 `@AppStorage("hasCompletedOnboarding")` 单独决定是否进入主界面
- 登录状态必须由 `AppSession` + Keychain token 决定
- Token 不存 UserDefaults
- View 里不写网络请求，不直接处理 Apple 登录细节

### 2.2 后端 API 对接

**现状**：所有页面为静态 UI，无任何网络请求。

**计划定义的 P0 API**：
- `POST /ai/analyze-learning-input` — AI 分析用户学习输入
- `POST /ai/chat` — AI 对话
- 用户/知识库/学习记录/反馈 CRUD

### 2.3 真实 AI 集成

**现状**：AI 相关页面全为静态文本。

**需对接**：
- 后端 AI Provider 抽象层（MiniMax/DeepSeek/OpenAI 等）
- 结构化 JSON 输出解析
- AI 分析结果展示（掌握度评分、优缺点、建议）
- AI 对话流式响应

### 2.4 本地数据持久化

**现状**：零持久化实现。

**需实现**：
- UserDefaults / @AppStorage（简单偏好）
- Keychain（Token、敏感信息）
- 后续可考虑 CoreData 或 SwiftData（学习记录离线缓存）

### 2.5 多语言本地化

**现状**：所有文案硬编码在 View 中，无 Localizable.xcstrings 文件。

**计划要求**（`Demo与MVP.md` 第 6 节）：
- 默认简体中文
- 预留英文
- App UI 文案使用本地化资源

**需实现**：
- 创建 `Localizable.xcstrings`
- 将所有硬编码文案迁移为 `LocalizedStringKey`
- 支持语言切换

### 2.6 错误/加载/空状态处理

**现状**：无任何错误处理、加载态、空状态 UI。

**至少需要**：
- 网络请求 loading 指示器
- 网络错误提示和重试按钮
- AI 分析中的等待状态
- 列表空状态（如知识库为空时的引导）
- 登录失败错误提示

---

## 三、页面层面差距

### 3.1 与计划页面对比

| 计划页面 | 计划优先级 | 当前状态 | 说明 |
|----------|-----------|---------|------|
| 启动页/欢迎页 | P1 | ✅ 已实现 | SplashPage + WelcomePage |
| 登录页 | P0 | ✅ 已实现 | 新 LoginView 替换旧 LoginPage，仅 Apple 登录 |
| 语言与偏好页 | P1 | ❌ 未实现 | 无页面 |
| 学习方向选择页 | P0 | ⚠️ 部分实现 | GoalSetupPage 有目标选择，但非学习方向选择 |
| 学习路径页 | P0 | ✅ 已实现 | LibraryDetailPage |
| 今日学习任务页 | P0 | ✅ 已实现 | StudyHomeView |
| 内容阅读页 | P0 | ✅ 已实现 | KnowledgeDetailPage |
| 主动回忆/笔记输入页 | P0 | ✅ 已实现 | DailyThinkingPage + RecallTestPage |
| AI 分析结果页 | P0 | ✅ 已实现 | AIFeedbackPage |
| AI 对话页 | P0 | ✅ 已实现 | AIChatPage |
| 复习计划页 | P0 | ✅ 已实现 | ReviewPlanView，含今天/明天/本周分组 + 复习类型标签 |
| 学习进度页 | P1 | ✅ 已实现 | AnalysisHomeView |
| 设置页 | P1 | ⚠️ 部分实现 | ProfileView 有设置菜单，但功能入口为空 |
| 反馈页 | P1 | ✅ 已实现 | FeedbackView + FeedbackViewModel，分类选择 + 提交确认 |

### 3.2 复习计划页（P0 ✅）

**计划描述**：系统生成复习任务，用户查看待复习内容，按推荐时间安排学习。

**当前**：已实现 `Features/Review/ReviewPlanView.swift`，含今天/明天/本周三组、复习类型标签（间隔重复/费曼/回忆/薄弱）、完成勾选、播放按钮。数据层使用 `ReviewTask` Model + mock 数据。

**待对接**：接入 ReviewService 后端数据。

### 3.3 反馈页（P1 ✅）

**计划描述**：App 内反馈入口，让内测用户提交问题和建议。

**当前**：已实现 `Features/Feedback/FeedbackView.swift` + `FeedbackViewModel.swift`，含 4 类反馈分类（Bug/功能建议/内容问题/其他）的图标选择器、文本描述输入、提交通知。入口位于 ProfileView 菜单末项。

**待对接**：接入后端 `/feedback` API（FeedbackService）。

### 3.4 等待名单入口

**计划**：官网 `/waitlist` 页面收集用户，App 内也需要引导用户加入等待名单/申请内测。

**需考虑**：是否在 App 内嵌等待名单入口（如 Welcome 页或设置页）。

---

## 四、设计与交互差距

### 4.1 Tab 结构调整

**计划设计**：4 个 Tab — 学习 | 知识库 | AI助手 | 我的

**当前实现**：5 个 Tab — AI | 知识库 | 学习 | 分析 | 我的

**差异分析**：
- 当前把"学习"和"分析"拆成了两个独立 Tab
- 计划把"AI助手"独立为一个 Tab，当前 AI 已是独立 Tab
- "分析"在计划中属于"学习"Tab 下的子页面，不需要顶层 Tab

**建议**（两种方案）：
- **方案 A**：完全对齐计划 → 合并学习和分析为一个 Tab，保持 4 Tab
- **方案 B**：保留 5 Tab 结构 → 更新计划文档，论证"分析"独立为 Tab 的合理性（学习数据可视化、学习进度监控是独立价值）

### 4.2 登录流程简化（已有详细设计）

**计划要求**：仅 Sign in with Apple，不做手机号/邮箱/微信登录。

**当前 UI**：包含 4 种登录方式入口。

**最终方案**（详见 `docs/AI对话.md`）：
- 登录页只保留 Sign in with Apple 一个按钮
- 删除手机号、邮箱、微信、验证码登录
- 跳过按钮仅限 `#if DEBUG`，Release 不展示
- 登录页文案极简：品牌 + 一句话价值主张 + Apple 登录按钮 + 协议入口

### 4.3 浅色/深色模式双主题（P1 新增）✅ 已完成

**实现方式**：

| 改造项 | 状态 |
|--------|------|
| `Color(light:dark:)` 自适应 helper | ✅ `DesignTokens.swift` 新增基于 `UITraitCollection.userInterfaceStyle` 的动态颜色 |
| 28 个颜色 token 双主题化 | ✅ 背景(4) + 文字(9) + 边框(5) + 填充(6) + 品牌色/彩色半透不变 |
| 渐变自适应 | ✅ `page`/`splash` 渐变改用自适应 Color token |
| 硬编码色值替换 | ✅ 13 处内联 `Color(hex:)` 替换为自适应 token |
| ColorSchemeManager | ✅ `Core/Appearance/ColorSchemeManager.swift`，@AppStorage 持久化，支持系统/浅色/深色 |
| 移除强制深色 | ✅ 移除 4 处 `.preferredColorScheme(.dark)` |
| 设置页切换入口 | ✅ ProfileView 外观行改为可点击，confirmationDialog 三选一 |

**文件变更**：
- 新增：`Core/Appearance/ColorSchemeManager.swift`
- 修改：`DesignTokens.swift`（颜色全量自适应 + 渐变）
- 修改：`AIStudyAppApp.swift`（根视图用 manager 控制 scheme）
- 修改：`ContentView.swift`、`LoginView.swift`（移除强制暗黑）
- 修改：`ProfileView.swift`（外观切换入口）
- 修改：`SplashPage.swift`、`ZXTabBar.swift`、`ZXAIInteractionRow.swift`、`ZXChartView.swift`、`AIFeedbackPage.swift`、`WelcomePage.swift`、`OnboardingPage.swift`、`GoalSetupPage.swift`、`ZXSTaskRow.swift`、`ZXIconBtn.swift`（内联色值→token）

### 4.4 语言系统（P1 新增）

**现状**：所有文案硬编码中文在 SwiftUI View 中，无 Localizable.strings。

**要求**：先只支持中文，但搭建好本地化基础设施，后续加语言时只需加翻译文件。

**需实现**：

| 步骤 | 说明 |
|------|------|
| 创建 `Localizable.strings` (Base) | 中文作为 Base 语言，不设 `zh-Hans` |
| 封装 `ZXLocalized` 辅助 | `String(localized:)` + `Text("key")` 的 SwiftUI 原生方式 |
| 迁移硬编码文案 | 逐文件将文案替换为 LocalizedStringKey |
| 设置页语言入口 | 预留语言切换 UI，当前仅显示"中文" |

### 4.5 无障碍

**现状**：未考虑 VoiceOver、Dynamic Type、高对比度等无障碍需求。

**至少需做**：
- 关键按钮添加 `.accessibilityLabel`
- 确保 Dynamic Type 下布局不破碎
- 重点页面 VoiceOver 测试

### 4.6 样式规范

已于 `docs/样式规范.md` 中梳理完整的样式规范文档，涵盖：
- 色彩系统（背景/文字/品牌语义色/边框/填充）
- 渐变体系（页面、品牌、卡片、进度条、CTA 等 11 组渐变）
- 圆角、间距、尺寸 token
- 字体层级（12 级定义）
- 共享组件目录（20+ 组件，含导航、按钮、卡片行、数据展示、输入、标签等）
- 页面布局模式（主 Tab 页、子页面、卡片、输入框、状态标签的标准写法）
- 设计决策与约束

**后续新页面必须遵循 `docs/样式规范.md`**，复用已有组件和 token，禁止随意使用内联颜色/间距/字体。

### 4.7 动效

**计划要求**（`官网与技术基础.md` 第 6.3 节）：
- P0：页面过渡、按钮反馈、加载状态、AI 分析中状态、学习完成反馈
- P1：今日任务卡片动效、进度条更新、AI 结果分块出现

**当前**：仅有基础 SwiftUI 隐式动画（withAnimation），未实现任何计划中的动效。

---

## 五、数据层缺失

### 5.1 Model 定义

**现状**：所有数据通过 View 内局部变量或硬编码定义，无独立 Model 文件。

**计划中定义的核心实体**（`Demo与MVP.md`）：

```
User
├── id, appleUserId, displayName, email
├── preferredLanguage, createdAt, lastLoginAt, status

KnowledgeBase
├── id, title, description, language, targetUser
├── createdAt, updatedAt

LearningPath
├── id, knowledgeBaseId, title, description
├── estimatedDays, order

Lesson
├── id, pathId, title, content, objectives
├── keyPoints, recallQuestions, practicePrompt
├── order, estimatedMinutes

LearningSession
├── id, userId, lessonId
├── startedAt, endedAt, userInput
├── aiAnalysis, masteryScore, weakPoints
├── nextSuggestion, reviewAt

AIAnalysis
├── id, userId, sessionId
├── inputText, outputJson, masteryScore
├── weakPoints, suggestions
├── modelName, createdAt, costEstimate

ReviewTask
├── id, userId, lessonId, sourceSessionId
├── reviewType, scheduledAt, completedAt, status

Feedback
UserLearningProfile
```

**需实现**：在 `Features/*/Models/` 下创建对应的 Swift struct（需 Codable、Identifiable）。

### 5.2 API Contract

**现状**：无 API 类型定义。

**建议**：参考计划中定义的 JSON 结构，先创建 Swift Model，再定义 API 请求/响应类型（Request/Response struct），实现前后端类型同构。

### 5.3 数据流规范

**现状**：View 直接持有 @State，无数据流管理。

**建议**：
- ViewModel 持有 @Published 状态
- ViewModel 通过 Service 获取数据
- Service 通过 Repository 访问数据源
- View 通过 @StateObject / @ObservedObject 绑定 ViewModel

---

## 六、工程化缺失

### 6.1 大文件拆分 ✅

**已完成**：

| 原文件 | 拆分结果 |
|--------|----------|
| `AIStudyAppApp.swift` (~190行) | 保留 App/Root/OnboardingFlowView，页面移入 `Features/Onboarding/` |
| `DailyThinkingPage.swift` (~200行) | 拆为 5 个文件（DailyThinking / RecallTest / WeakPoints / AIFeedback / AIChat） |
| `LibrarySubpages.swift` (~112行) | 已删除，拆为 6 个独立页面文件 |

### 6.2 共享组件管理 ✅

**已完成**：20 个共享组件集中到 `Shared/Components/`，原文件中的定义已移除。

```
Shared/Components/
├── ZXTabBar.swift          ├── ZXBackHeader.swift
├── ZXIconBtn.swift         ├── ZXScoreBox.swift
├── ZXAIInputBar.swift      ├── ZXOutlineBtn.swift
├── ZXQuickAction.swift     ├── ZXAIInteractionRow.swift
├── ZXCardRow.swift         ├── ZXChip.swift
├── ZXImportOption.swift    ├── ZXWeakRow.swift
├── ZXStatBadge.swift       ├── ZXProfileStat.swift
├── ZXProfileMenuRow.swift  ├── ZXAchievementBadge.swift
├── ZXChartView.swift       ├── ZXSTaskRow.swift
├── FeatureRow.swift        ├── ReviewTaskRow.swift
├── ZXLoadingView.swift     ├── ZXErrorView.swift
└── ZXEmptyView.swift
```

### 6.3 测试

**现状**：无任何测试代码。

**至少需要**：
- ViewModel 单元测试（当 ViewModel 创建后）
- Service 层单元测试（Mock Repository）
- 关键 UI 流程的 Snapshot 测试

### 6.4 CI/CD

**现状**：无。

**建议**（后续）：
- GitHub Actions / Xcode Cloud 自动构建
- TestFlight 自动分发

### 6.5 崩溃监控与埋点

**现状**：无。

**建议**：接入 Firebase Crashlytics 或类似服务，至少在 TestFlight 阶段要有崩溃收集能力。

---

## 七、优先级建议

### P0 — 必须在接后端前完成

| 步骤 | 项目 | 理由 | 设计文档 |
|------|------|------|----------|
| ① | 创建 Auth Model（AuthModels + User） | 所有后续步骤的数据基础 | 第十章 步骤 1 ✅ |
| ② | 实现 Keychain 存储层 | Token 安全存储是登录的前提 | 第十章 步骤 2 ✅ |
| ③ | 搭建网络层最小实现（APIClient） | 所有后端交互的唯一通道 | 第十章 步骤 3 ✅ |
| ④ | 实现 AuthService（Apple 登录 + 后端调用） | 用户身份是学习记录的前提 | `docs/AI对话.md`、第十章 步骤 4 ✅ |
| ⑤ | 实现 AppSession（全局登录状态） | 统一的登录态管理 | 第十章 步骤 5 ✅ |
| ⑥ | 实现 LoginView + LoginViewModel | 替换当前过度实现的登录页 | `docs/AI对话.md`、第十章 步骤 6 ✅ |
| ⑦ | 改造 App 入口启动逻辑 | Token 分流替换 @AppStorage | 第十章 步骤 7 ✅ |
| — | 创建 Model 层（其余数据实体） | 是 Service/ViewModel/API 的基础 | 本文档 5.1 节 ✅ |
| — | 实现复习计划独立页 | 计划标记 P0 | ✅ |
| — | 拆分大文件 + 集中共享组件 | 降低后续修改的认知负担 | 本文档 6.1/6.2 节 ✅ |
| — | 添加加载/错误/空状态处理 | 真机使用的基本体验保障 | `Shared/Components/` (ZXLoadingView, ZXErrorView, ZXEmptyView) ✅ |

### P1 — 与后端对接同步推进

| 优先级 | 项目 | 理由 |
|--------|------|------|
| P1 | 浅色/深色模式双主题 | 覆盖所有页面和 DesignTokens，工作量较大 ✅ |
| P1 | 语言系统搭建（中文 Base） | 先建基础设施，后续加语言不返工 ✅ |
| P1 | 搭建 ViewModel 层（逐步迁移） | 架构分层，但不阻塞功能开发 ✅ |
| P1 | 搭建 Service 层 | 随 API 对接自然建立 ✅ |
| P1 | 实现反馈页 | TestFlight 内测必须 ✅ |
| P1 | 实现设置页完整功能 | 外观切换、语言入口、复习提醒等 ✅ |

### P2 — App Store 前完成

| 优先级 | 项目 | 理由 |
|--------|------|------|
| P2 | Repository 层 | 当需要本地缓存 + 网络切换时再做 ✅ |
| P2 | 动效补充 | 体验优化，不阻塞功能 ✅ |
| P2 | 无障碍适配 | App Store 审核加分项 ✅ |
| P2 | 测试 | 用户量增长后需要 ✅ |
| P2 | Tab 结构调整决策 | 需要更多用户反馈来决策 ✅ |

### Tab 结构分析

**当前 5-Tab 结构**：

| Tab | 页面 | 核心功能 |
|-----|------|----------|
| AI | AIHomeView | AI 对话入口 + 每日思考 + 薄弱点 |
| 知识库 | LibraryHomeView | 知识库浏览 + 导入 + 搜索 |
| 学习 | StudyHomeView | 今日任务 + 进度 + 每周活跃 |
| 分析 | AnalysisHomeView | 学习时长 + 掌握度 + 雷达图 |
| 我的 | ProfileView | 个人信息 + 设置 + 成就 |

**问题诊断**：
1. **AI 与学习边界模糊** — AI 对话产生学习记录，但学习任务在独立 Tab，用户需要在两个 Tab 间切换
2. **分析 Tab 内容单薄** — 纯展示仪表盘，无交互深度，与"学习"Tab 的进度卡片有重叠
3. **知识库入口过重** — 知识库本质是学习的前置步骤，独立 Tab 使其脱离学习流程

**可选方案**：

| 方案 | 结构 | 优点 | 缺点 |
|------|------|------|------|
| A: 保持现状 | 5 Tab 不变 | 无改动成本 | 上述问题持续 |
| B: 合并 AI+学习 | 4 Tab（学习/AI、知识库、分析、我的） | AI 与学习一体化 | 需重设计学习首页 |
| C: 合并分析入学习 | 4 Tab（AI、知识库、学习+分析、我的） | 分析数据有上下文 | 学习页信息密度增加 |
| D: 精简 3 Tab | 3 Tab（学习、知识库、我的） | 最简洁，AI 内嵌学习 | 分析页降级为次级入口 |

**建议**：MVP 阶段保持方案 A，收集用户反馈后优先尝试方案 C（分析并入学习）。触发条件：分析 Tab 的周活跃用户 < 20%。

---

## 八、总结

当前 iOS 项目完成了 UI 层的全量搭建（21 页），但缺少"能让产品真正运转"的架构底座和数据能力。核心矛盾是：

> UI 超前，架构滞后。页面能点，但无数据、无认证、无 AI、无服务。

### 当前进度（2026-05-10）

**P0 — 全部完成 ✅**：
1. ✅ Apple 登录 + Auth 体系（9 个文件）
2. ✅ Model 层（10 个数据实体）
3. ✅ 网络层最小实现（APIClient + Endpoint + Error）
4. ✅ App 入口重构（AppSession 驱动路由，替代 @AppStorage）
5. ✅ 复习计划独立页（ReviewPlanView）
6. ✅ 大文件拆分（3 个大文件拆为 15+ 个独立文件）
7. ✅ 共享组件集中管理（`Shared/Components/` 下 20+ 个组件）
8. ✅ 加载/错误/空状态处理（ZXLoadingView / ZXErrorView / ZXEmptyView）

**P1 — 部分完成**：
- ✅ 浅色/深色模式双主题（DesignTokens 自适应 + ColorSchemeManager + 移除强制暗黑）
- ✅ 语言系统搭建（中文 Base，Localizable.strings + ZXStrings + LanguageManager）
- ✅ 搭建 ViewModel 层（ReviewPlanViewModel + AIChatViewModel + StudyHomeViewModel）
- ✅ 搭建 Service 层（5 个 Service 协议 + 实现 + 20 个 APIEndpoint）
- ✅ 实现反馈页（TestFlight 内测必须）
- ✅ 设置页外观切换（ColorSchemeManager + ProfileView confirmationDialog）
- ✅ 设置页完整功能（5 个子页面 + 外观/语言切换）

**接下来推荐顺序**：

```
1. 语言系统搭建（基础设施优先，避免后续返工）
   ├── 创建 Localizable.strings (Base = 中文)
   ├── 迁移硬编码文案到 LocalizedStringKey
   └── 设置页预留语言入口（当前仅显示中文）
       ↓
2. 浅色/深色模式双主题（体验升级，需全量回归）
   ├── DesignTokens 定义 light/dark 双套色值
   ├── 替换所有内联 Color(hex:) 为 token 引用
   ├── 设置页新增外观切换（跟随系统 / 浅色 / 深色）
   └── 全页面浅色模式验证
       ↓
3. 反馈页 + 设置页补全（独立页面，不依赖其他改造）
       ↓
4. ViewModel 层迁移（逐步，不阻塞功能）
       ↓
5. Service 层搭建（随 API 对接自然建立）
```

---

## 九、AI对话.md 登录方案摘要

> `docs/AI对话.md` 是登录模块的详细实现规范。以下为关键决策的结构化提取，便于对照实施。

### 9.1 登录入口决策

**第一版只保留**：
```
Sign in with Apple
```

**删除这些入口**：
- 手机号登录 / 邮箱登录 / 微信登录 / 验证码登录

**"跳过"按钮**：
- `#if DEBUG` 保留，Release 不展示
- 理由：避免匿名用户后续迁移（学习记录、AI 分析绑定用户身份）

### 9.2 登录页内容

```
知习

更懂你，更会学。

用 AI 把知识库、主动回忆和间隔复习连接起来，
从"看过"走向"真正学会"。

[ Sign in with Apple ]

登录即代表你同意《用户服务协议》和《隐私政策》
```

### 9.3 完整登录流程

```
App 启动
    ↓
AppSession 检查 Keychain 是否有 refreshToken
    ↓
有 token → 调用 /auth/refresh 或 /users/me
    ├─ 成功 → 进入主界面
    └─ 失败 → 清空 token，进入登录页
    ↓
无 token → 进入登录页
    ↓
用户点击 Sign in with Apple
    ↓
ASAuthorizationController 获取：
  · identityToken
  · authorizationCode
  · userIdentifier
  · email / fullName（Apple 可能不返回）
    ↓
POST /api/auth/apple
    ↓
后端返回 { accessToken, refreshToken, expiresIn, user }
    ↓
accessToken / refreshToken 存入 Keychain
    ↓
判断 user.onboardingCompleted
    ├─ false → 引导页 / 学习目标设置
    └─ true  → 主界面（ContentView）
```

### 9.4 需要新增的 9 个文件

| 层 | 文件 | 职责 |
|----|------|------|
| Model | `Core/Models/AuthModels.swift` | AppleLoginRequest、AuthResponse 等 Codable struct |
| Model | `Core/Models/User.swift` | 用户实体 |
| Storage | `Core/Storage/KeychainStore.swift` | 通用 Keychain 读写封装 |
| Storage | `Core/Storage/TokenStore.swift` | Token 专用存取（save/load/clear） |
| Service | `Core/Services/AuthServiceProtocol.swift` | AuthService 协议定义 |
| Service | `Core/Services/AuthService.swift` | ASAuthorizationController 集成 + 后端调用 |
| App | `App/AppSession.swift` | @MainActor 全局登录状态 |
| View | `Features/Auth/Views/LoginView.swift` | 纯 Apple 登录按钮 UI |
| ViewModel | `Features/Auth/ViewModels/LoginViewModel.swift` | @Published isLoading/errorMessage |

### 9.5 API Contract

```swift
// 请求 → POST /api/auth/apple
struct AppleLoginRequest: Encodable {
    let identityToken: String
    let authorizationCode: String?
    let userIdentifier: String
    let fullName: AppleFullName?
    let email: String?
}

struct AppleFullName: Encodable {
    let givenName: String?
    let familyName: String?
}

// 响应
struct AuthResponse: Decodable {
    let accessToken: String
    let refreshToken: String
    let expiresIn: Int
    let user: User
}
```

### 9.6 关键约束

| 约束 | 说明 |
|------|------|
| Token 存 Keychain | 不存 UserDefaults |
| 不用 @AppStorage 控制登录 | 登录状态由 AppSession + Keychain token 决定 |
| View 不写网络请求 | 网络调用在 Service 层 |
| View 不处理 Apple 登录细节 | ASAuthorizationController 逻辑在 AuthService |
| 不改变现有主页面 UI | 只替换入口路由逻辑 |

---

## 十、登录模块可行实施计划

以下是按依赖关系排列的 7 个实施步骤。每步独立可验证，后一步依赖前一步完成。

### 整体依赖图

```
步骤1: Model ─────────────────────────────────────────┐
       ↓                                               │
步骤2: KeychainStore / TokenStore ────────────────────┤
       ↓                                               │
步骤4: APIClient / APIEndpoint ─┐                      │
       ↓                        │                      │
步骤3: AuthService ←────────────┘                      │
       ↓                                               │
步骤5: AppSession ─────────────────────────────────────┤
       ↓                                               │
步骤6: LoginView + LoginViewModel ────────────────────┘
       ↓
步骤7: 改造 App 入口启动逻辑（AIStudyAppApp.swift）
```

---

### 步骤 1：创建 Auth Model ✅ 已完成（2026-05-10）

**产出文件**：
- `Core/Models/AuthModels.swift`
- `Core/Models/User.swift`

**内容**：
```swift
// AuthModels.swift
struct AppleLoginRequest: Encodable { ... }
struct AppleFullName: Encodable { ... }
struct AuthResponse: Decodable { ... }

// User.swift
struct User: Codable, Identifiable {
    let id: String
    let appleUserId: String
    let displayName: String?
    let email: String?
    let preferredLanguage: String
    let onboardingCompleted: Bool
    let createdAt: String
    let lastLoginAt: String?
    let status: String
}
```

**依赖**：无

**验证**：Xcode 编译通过

---

### 步骤 2：实现 Keychain 存储层 ✅ 已完成（2026-05-10）

**产出文件**：
- `Core/Storage/KeychainStore.swift`
- `Core/Storage/TokenStore.swift`

**KeychainStore 职责**：通用 Keychain 读写，封装 `SecItemAdd`/`SecItemCopyMatching`/`SecItemDelete`，支持 save/load/delete 操作。

**TokenStore 职责**：
```swift
protocol TokenStoreProtocol {
    func saveAccessToken(_ token: String) throws
    func getAccessToken() throws -> String?
    func saveRefreshToken(_ token: String) throws
    func getRefreshToken() throws -> String?
    func clearAll() throws
}
```

**依赖**：步骤 1（Model 定义，具体来说不需要 Model 依赖，TokenStore 操作的是原始 String）

**验证**：可写简单单元测试验证存取清除

---

### 步骤 3：搭建网络层最小实现 ✅ 已完成（2026-05-10）

> 注意：这一步和 AuthService 互相依赖——AuthService 需要 APIClient 发请求，但可以先建网络层骨架。做的时候步骤 3 和 4 可以部分并行：先建 APIClient 基础，再写 AuthService 时补充 Auth 相关 endpoint。

**产出文件**：
- `Core/Network/APIClient.swift`
- `Core/Network/APIEndpoint.swift`
- `Core/Network/APIError.swift`

**最小接口**：
```swift
// APIClient
class APIClient {
    init(baseURL: URL, tokenStore: TokenStoreProtocol?)
    func request<T: Decodable>(_ endpoint: APIEndpoint) async throws -> T
    func requestVoid(_ endpoint: APIEndpoint) async throws
}

// APIEndpoint
enum APIEndpoint {
    case appleLogin(AppleLoginRequest)
    case refreshToken(String)
    case me
    // 后续扩展其他 endpoint
}

// APIError
enum APIError: Error {
    case network(Error)
    case httpError(Int)
    case decoding(Error)
    case unauthorized
}
```

**依赖**：步骤 1（Model）、步骤 2（TokenStore）

**验证**：Xcode 编译通过，可以先 mock 一个请求验证 pipeline 跑通

---

### 步骤 4：实现 AuthService ✅ 已完成（2026-05-10）

**产出文件**：
- `Core/Services/AuthServiceProtocol.swift`
- `Core/Services/AuthService.swift`

**AuthServiceProtocol**：
```swift
protocol AuthServiceProtocol {
    func loginWithApple() async throws -> AuthResponse
    func refreshSession() async throws -> AuthResponse
    func logout() async throws
    func fetchCurrentUser() async throws -> User
}
```

**AuthService 实现要点**：
1. 集成 `ASAuthorizationController`（需 `import AuthenticationServices`）
2. 获取 identityToken、authorizationCode、userIdentifier
3. 调用 `APIClient.request(.appleLogin(request))`
4. 将返回的 token 写入 TokenStore
5. refreshSession：用 refreshToken 换新 token

**依赖**：步骤 1（AuthModels）、步骤 2（TokenStore）、步骤 3（APIClient）

**验证**：Xcode 编译通过，可在模拟器点击 Apple 登录（后端未就绪时用 mock）

---

### 步骤 5：实现 AppSession ✅ 已完成（2026-05-10）

**产出文件**：
- `App/AppSession.swift`

**关键代码骨架**：
```swift
@MainActor
final class AppSession: ObservableObject {
    @Published var currentUser: User?
    @Published var isAuthenticated = false
    @Published var isLoading = true
    @Published var authError: String?

    private let authService: AuthServiceProtocol
    private let tokenStore: TokenStoreProtocol

    func bootstrap() async {
        // 1. 检查 Keychain 是否有 refreshToken
        // 2. 有 → 调用 refreshSession()
        // 3. 成功 → isAuthenticated=true, currentUser=user
        // 4. 失败 → 清空 token, isAuthenticated=false
        // 5. 无 → isAuthenticated=false
        // 6. isLoading = false
    }

    func loginWithApple() async { ... }
    func logout() { ... }
}
```

**依赖**：步骤 4（AuthService）、步骤 2（TokenStore）

**验证**：模拟器启动时可根据 Keychain 状态正确分流

---

### 步骤 6：实现 LoginView + LoginViewModel ✅ 已完成（2026-05-10）

**产出文件**：
- `Features/Auth/Views/LoginView.swift`
- `Features/Auth/ViewModels/LoginViewModel.swift`

**LoginView**：
- 品牌标题"知习" + 副标题
- Sign in with Apple 按钮（ASAuthorizationAppleIDButton）
- Loading 状态（ProgressView）
- Error 提示
- `#if DEBUG` 跳过按钮
- 协议入口链接

**LoginViewModel**：
```swift
@MainActor
final class LoginViewModel: ObservableObject {
    @Published var isLoading = false
    @Published var errorMessage: String?

    func loginWithApple() async {
        isLoading = true
        errorMessage = nil
        do {
            try await appSession.loginWithApple()
        } catch {
            errorMessage = "登录失败：\(error.localizedDescription)"
            isLoading = false
        }
    }
}
```

**依赖**：步骤 5（AppSession）

**验证**：模拟器显示登录页，点击 Apple 登录按钮触发流程，loading 状态可展示

---

### 步骤 7：改造 App 入口启动逻辑 ✅ 已完成（2026-05-10）

**修改文件**：`AIStudyAppApp.swift`

**改动要点**：
1. 注入 `AppSession` 为 `@StateObject`
2. 启动时调用 `appSession.bootstrap()`
3. 用 `appSession.isLoading / isAuthenticated / currentUser?.onboardingCompleted` 替换原来的 `@AppStorage("hasCompletedOnboarding")`
4. 路由逻辑：
   ```
   isLoading          → Splash（启动加载中）
   !isAuthenticated   → LoginView
   onboardingCompleted == false → OnboardingFlowView
   onboardingCompleted == true  → ContentView
   ```

**依赖**：步骤 1-6 全部完成

**验证**：完整启动流程测试——
- 首次启动 → 登录页 → Apple 登录（mock） → 引导页 → 目标设置 → 主界面
- 二次启动（有有效 token）→ 直接进主界面
- Token 过期 → 登录页

---

### 实施俯视图

```
步骤 1 ──→ 步骤 2 ──→ 步骤 3 ──→ 步骤 4 ──→ 步骤 5 ──→ 步骤 6 ──→ 步骤 7
Model     Keychain   网络层    AuthSvc   AppSession  LoginUI   入口改造
(0依赖)   (无依赖)   (依赖1,2) (依赖1-3)  (依赖2,4)  (依赖5)   (依赖1-6)
```

每一步均可独立编译验证。后端 API 未就绪时，步骤 4-7 可以用 mock 数据先行开发，后端就绪后仅替换 APIClient 的真实 endpoint。

### 文件目录结构（完成后）

```
AIStudyApp/
├── App/
│   └── AppSession.swift              ← 新增
├── Core/
│   ├── Models/
│   │   ├── AuthModels.swift          ← 新增
│   │   └── User.swift                ← 新增
│   ├── Network/
│   │   ├── APIClient.swift           ← 新增
│   │   ├── APIEndpoint.swift         ← 新增
│   │   └── APIError.swift            ← 新增
│   ├── Services/
│   │   ├── AuthServiceProtocol.swift ← 新增
│   │   └── AuthService.swift         ← 新增
│   ├── Storage/
│   │   ├── KeychainStore.swift       ← 新增
│   │   └── TokenStore.swift          ← 新增
│   └── DesignSystem/
│       └── DesignTokens.swift        （已有，不变）
├── Features/
│   ├── Auth/
│   │   ├── Views/
│   │   │   └── LoginView.swift       ← 新增
│   │   └── ViewModels/
│   │       └── LoginViewModel.swift   ← 新增
│   ├── AI/          （已有）
│   ├── Library/     （已有）
│   ├── Study/       （已有）
│   ├── Analysis/    （已有）
│   └── Profile/     （已有）
└── AIStudyAppApp.swift               ← 修改
```
