# 知习后端：当前状态 & 需要你处理的决策和任务

> 基于代码审计生成。**2026-05-16 已确认所有决策，执行中。**

---

# 一、当前代码状态总览

## 已完成（真实可用的）

| 模块 | 状态 | 说明 |
|------|------|------|
| Auth 控制器 + 服务 | 真实 | dev-login / apple / refresh / logout 全部写好了 |
| TokenService | 真实 | JWT accessToken + crypto refreshToken + hash 入库 |
| JwtAuthGuard | 真实 | 可正常验证 Bearer token |
| CurrentUser 装饰器 | 真实 | 从 request.user 提取当前用户 |
| AppleAuthService | 真实+兜底 | BUNDLE_ID 有值就走真实 JWKS 验证，没值走 mock |
| UsersRepository | 真实 | **唯一接 Prisma 的 repository** |
| PrismaService | 真实 | 连接 MySQL |
| Prisma Schema | 完整 | 所有表都定义了（User, KnowledgeBase, KnowledgeItem, 等 30+ 个 model） |
| AppModule | 完整 | 所有模块都注册了 |
| 基础设施骨架 | 有 | Redis/Storage/Logger/Queue 模块都创建了 |
| 全局异常过滤器 | 有 | |
| 统一响应拦截器 | 有 | |
| DTO 校验管道 | 有 | |

| Apple 登录配置 | 有 | 但缺 `APPLE_BUNDLE_ID` |

## 仍在使用内存 Map（没有接数据库）

| 模块 | 存储方式 | 严重程度 |
|------|----------|----------|
| KnowledgeBaseRepository | `new Map()` | 高 |
| KnowledgeItemsRepository | `new Map()` | 高 |
| ActiveRecallRepository | `new Map()` × 2 | 高 |
| AiAnalysisRepository | `new Map()` × 2 | 高 |
| FocusItemsRepository | `new Map()` | 高 |
| ReviewRepository | `new Map()` × 2 | 高 |
| LearningSessionRepository | `new Map()` | 高 |
| LearningActivityRepository | `new Map()` | 中 |
| DocumentImportRepository | `new Map()` | 中 |
| QueueService | `new Map()` | 中 |

## AI 模块 ✅ 已升级为三层架构

| 组件 | 状态 |
|------|------|
| AiProvider 接口 | ✅ 统一 generate() + signal |
| MockAiProvider | ✅ 永久保留 |
| DeepSeekProvider | ✅ OpenAI 兼容协议 |
| MiniMaxProvider | ✅ OpenAI 兼容协议（Coding Plan） |
| ModelRouter | ✅ cheap / primary / strong 三档 |
| AiGatewayService | ✅ 统一入口 + JSON容错 + 超时重试 |
| PromptTemplateService | ✅ key + version 注册 |
| ActiveRecallAnalysisWorkflow | ✅ 第一个 Workflow |
| AiUsageLog + CostCalculator | ✅ 已加入 schema |
| 旧 infrastructure/ai | ❌ 已删除 |
| 新 AiModule | ✅ `src/modules/ai/` 14 个文件 |

详见 `startup-plan/技术设计/api-server/[已完成]-AI架构决策清单.md`

---

# 二、需要你决策的事项

这些都是我无法替你决定的问题，每一个都会影响后续开发方向。

## 决策 1：MySQL 数据库 —— 当前什么状态？

`.env` 里配置了：
```
DATABASE_URL="mysql://zhixi_user:Zhixi@2026!App@localhost:3306/zhixi"
```

需要确认：
- [ ] MySQL 服务是否在运行？
- [ ] 数据库 `zhixi` 是否已创建？
- [ ] 数据库里是否已有数据？（如果之前跑过可能会有旧表）
- [ ] Prisma schema 和实际数据库是否一致？

**为什么重要**：Prisma schema 已定义 30+ 张表，但没有 migrations 目录。第一次 `prisma migrate dev` 会生成初始 migration。如果数据库已有表结构（非 Prisma 迁移创建的），需要额外处理。

**建议方案**：
- 如果是空库 → 直接 `prisma migrate dev` 生成初始迁移
- 如果已有 Prisma 创建的表 → 先 `prisma db pull` 拉取现有结构，再 `prisma migrate dev`
- 如果 MySQL 没启动 → 先解决 MySQL 启动问题

---

## 决策 2：BigInt ID 问题 —— 现在不改，后面全是雷

Prisma schema 所有主键都是 `BigInt @default(autoincrement())`。

但是：
- JavaScript/JSON 不支持 64 位整数
- Auth 模块里已经出现大量 `BigInt(userId)` / `String(user.id)` 的转换
- KnowledgeBase 的 in-memory 版本用的是 `string` ID
- JWT payload 里的 `sub` 是 `String(user.id)`
- 这意味着：**内存版本和数据库版本的 ID 类型不统一**

**问题场景**：
```
数据库返回: user.id = 9007199254740993n  (BigInt, 超出 JS 安全范围)
JSON.stringify → "9007199254740992"  (精度丢失!)
```

**选项**：
- A) 换 `String @id @default(cuid())` —— 最安全，推荐，但需要改 schema 和所有关联代码
- B) 换 `Int @default(autoincrement())` —— 简单但有上限（21 亿），够用但不够好看
- C) 保持 `BigInt` —— 不改，但要全局统一序列化（`JSON.stringify` 不能直接用），所有 repository 都要转 string

---

## 决策 3：AI Provider —— 第一个接哪个？

目前 AI 是纯 mock，返回固定 JSON。

**选项**：
- A) **DeepSeek** —— 便宜，中文强，适合预算有限的独立开发者
- B) **OpenAI (GPT-4o)** —— 贵但生态好，SDK 成熟
- C) **Anthropic Claude** —— 长上下文优秀，适合大段知识分析
- D) **MiniMax / 豆包 / 通义千问** —— 国内合规，需要 ICP 备案才能用

**连锁影响**：
- 决定了 AIGateway 第一个 provider 实现
- 决定了 Prompt 模板的调试环境
- 决定了成本（需要 API key 和充值）

---

## 决策 4：Redis 状态 —— 需要现在启动吗？

`.env` 里配置了 Redis 连接，`AiAnalysisService` 里用了 Redis（限流、任务状态、锁）。

但是这些功能**不启动 Redis 也能绕过去**——限流可以先去掉，任务状态可以先放内存。

**建议**：先不管 Redis，集中精力把数据库层搞定。限流等功能后面补。

---

## 决策 5：Prisma Schema 需要调整吗？

现在 schema 非常完整（30+ 张表），但很多是远期才用的表：

| 近期需要的表 | 远期才用的表 |
|-------------|-------------|
| users | UserProfile |
| auth_accounts | UserPreference |
| refresh_tokens | UserConsent |
| knowledge_bases | KnowledgeItemRelation |
| knowledge_items | Tag (如果第一版不做标签) |
| active_recall_questions | KnowledgeItemTag |
| active_recall_answers | UploadedFile |
| ai_analysis_jobs | DocumentImport |
| ai_analysis_results | LearningRecord |
| focus_items | ReviewPlan |
| review_cards | AppChangelog |
| review_logs | DailyLearningActivity |
| learning_sessions | Notification |
| feedback | ... |

**选项**：
- A) 保持全部 schema，一次迁移创建所有表（简单但有大量空表）
- B) 先只迁移近期需要的表（干净但需要改 schema 文件）

---

## 决策 6：Apple 登录什么时候接？

代码已写好，AppleAuthService 带了 mock 兜底。

问题：
- `APPLE_BUNDLE_ID` 为空 → Apple 登录走 mock
- 真正的 Apple ID token 验证需要 Apple Developer 账号
- 这取决于你的 Apple Developer 审核进度

**当前行为**：即使 APPLE_BUNDLE_ID 为空，Apple 登录接口也能用，只是不走真实验证。这对开发阶段来说够了。

---

# 三、当前无法处理的事情

这些事情需要外部条件满足才能推进：

| 阻塞项 | 原因 | 谁来解决 | 预计时间 |
|--------|------|----------|----------|
| 11 个 Repository 接 Prisma | 待逐一迁移 | 开发 | — |
| AiUsageLog 表建到服务器 | SSH 隧道未连通 | 你 | — |
| Apple 登录真实验证 | 需要 APPLE_BUNDLE_ID | Apple 审核 | 不确定 |
| BullMQ 队列 | 需要 Redis | 可推迟 | — |
| 文件上传 (COS/S3) | 需要选存储服务 | 可推迟 | — |
| iOS Keychain / AppSession | 前端配合 | iOS 开发时 | — |

## ✅ 已解决的阻塞项

| 阻塞项 | 状态 |
|--------|------|
| Prisma 首次迁移 | ✅ db push 完成，所有表已建 |
| 真实 AI 分析 | ✅ 三层架构已落地，Mock/DeepSeek/MiniMax 三 Provider |
| BigInt ID 方案 | ✅ 全部改为 String cuid() |

---

# 四、分阶段可执行任务清单

## 阶段 1：地基打通（现在就要做）

这些是最紧迫的，必须在写新功能之前完成。

### 任务 1.1：确认数据库连接 + 跑通首次迁移

**前置条件**：决策 1

```text
1. 确认 MySQL 服务已启动
2. 确认 zhixi 数据库已创建（不存在则 CREATE DATABASE zhixi）
3. 根据决策 2 调整 Prisma schema 的 ID 类型
4. 运行 prisma migrate dev --name init
5. 验证生成的 migration SQL 无误
6. 运行 prisma generate
7. 启动服务，调 dev-login 验证 auth 模块正常工作
```

### 任务 1.2：解决 BigInt ID 方案

**前置条件**：决策 2

如果是方案 A（cuid），需要改：
- `schema.prisma` 所有 `BigInt @default(autoincrement())` → `String @id @default(cuid())`
- `AuthService` 里的 `BigInt(userId)` 转换去掉
- `UsersRepository` 里的 `BigInt(userId)` 转换去掉
- `JwtAuthGuard` 的 `request.user` 类型

### 任务 1.3：把所有 Repository 从 Map 迁到 Prisma

**前置条件**：任务 1.1 + 1.2

按优先级迁：
```text
第一批（核心业务）：
  KnowledgeBaseRepository  → prisma
  KnowledgeItemsRepository → prisma
  ActiveRecallRepository   → prisma

第二批（学习闭环）：
  LearningSessionRepository → prisma
  AiAnalysisRepository      → prisma
  FocusItemsRepository      → prisma
  ReviewRepository          → prisma

第三批（辅助）：
  LearningActivityRepository → prisma
  DocumentImportRepository   → prisma
```

每个 repository 改造模板：
```text
1. 注入 PrismaService
2. create → prisma.xxx.create()
3. findById → prisma.xxx.findUnique()
4. findAllByUserId → prisma.xxx.findMany({ where: { userId } })
5. update → prisma.xxx.update()
6. softDelete → prisma.xxx.update({ data: { deletedAt: new Date() } })
7. 删除 Map 相关代码
```

### 任务 1.4：给所有 Controller 统一加 JwtAuthGuard

当前问题：
- `KnowledgeBaseController` 的 `@CurrentUser()` 标记了 `| undefined`，实际允许未登录访问
- 部分 Controller 根本没加 guard

```text
1. KnowledgeBaseController 加 @UseGuards(JwtAuthGuard)
2. KnowledgeItemsController 加 @UseGuards(JwtAuthGuard)
3. ActiveRecallController 加 @UseGuards(JwtAuthGuard)
4. AiAnalysisController 加 @UseGuards(JwtAuthGuard)
5. FocusItemsController 加 @UseGuards(JwtAuthGuard)
6. ReviewController 加 @UseGuards(JwtAuthGuard)
7. LearningSessionController 加 @UseGuards(JwtAuthGuard)
8. LearningActivityController 加 @UseGuards(JwtAuthGuard)
```

---

## 阶段 2：第一个真实 AI Provider

### 任务 2.1：选 provider + 获取 API Key

**前置条件**：决策 3

### 任务 2.2：实现第一个真实 AI Provider

```text
1. 创建 DeepSeekProvider（或你选的 provider）
2. 实现 AiProvider 接口
3. 对接真实 API
4. 处理超时、重试、JSON 解析
5. 更新 AiModule 根据配置切换 provider
```

### 任务 2.3：实现第一个真实 Prompt 模板

硬编码在 MockAiProvider 里的静态 JSON 不能用了，需要：
```text
1. 编写主动回忆分析的 System Prompt
2. 设计输出 JSON Schema
3. 处理 AI 返回格式不稳定的情况（JSON 解析错误兜底）
```

---

## 阶段 3：学习闭环联调

### 任务 3.1：验证端到端流程

```text
用户登录
→ 创建知识库
→ 创建知识点
→ 开始学习会话
→ 提交主动回忆
→ AI 分析
→ 生成 FocusItem
→ 生成复习卡
→ 完成复习
```

---

# 五、决策汇总表（已确认）

| 编号 | 问题 | 决策 | 状态 |
|------|------|------|------|
| 决策 1 | MySQL 数据库当前状态？ | URL-encode 密码 `@→%40, !→%21`，确认连接后 migrate | 已修复 DATABASE_URL |
| 决策 2 | BigInt vs cuid vs Int，用哪个 ID 方案？ | **全部 String cuid()**，不用 BigInt | 已完成 |
| 决策 3 | 第一个 AI Provider 选谁？ | DeepSeek，但先不接，等核心闭环跑通 | 已决策 |
| 决策 4 | Redis 现在启动还是先绕过？ | 先绕过，不阻塞主线 | 已决策 |
| 决策 5 | Prisma Schema 全部迁移还是分批？ | 保留完整 schema，一次迁移 | 已决策 |
| 决策 6 | Apple 登录什么时候接？ | **现在接**，BUNDLE_ID=cloud.longde.AIStudyApp | 已配置 |

## 已完成的改动

- [x] `schema.prisma`：全部 27 个 model 的 BigInt → String cuid()
- [x] `auth.service.ts`：删除 BigInt() 转换、bigint 类型 → string
- [x] `token.service.ts`：bigint → string，删除 String(user.id) 转换
- [x] `users.repository.ts`：删除所有 BigInt() 转换
- [x] `security.util.ts`：number|bigint → string
- [x] `.env`：DATABASE_URL 密码 URL-encode，补 APPLE_BUNDLE_ID
- [x] `.env.example`：同步更新

## 待完成（需要 MySQL 环境）

- [ ] `prisma migrate dev --name init`
- [ ] `prisma generate`
- [ ] 启动服务，验证 dev-login、refresh、logout、users/me
- [ ] KnowledgeBaseRepository 接 Prisma（第一个业务 repository 迁移）
- [ ] KnowledgeItemsRepository 接 Prisma
- [ ] ActiveRecallRepository 接 Prisma
- [ ] 所有 Controller 加 JwtAuthGuard
