# 缺失项与待补全方向 > 基于 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(_ 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 ← 修改 ```