361 lines
12 KiB
Markdown
361 lines
12 KiB
Markdown
|
|
# 知习后端:当前状态 & 需要你处理的决策和任务
|
|||
|
|
|
|||
|
|
> 基于代码审计生成。**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
|