# 收藏数据模型

<cite>
**本文引用的文件**
- [server\prisma\schema.prisma](file://server\prisma\schema.prisma)
- [server\src\modules\favorites\favorites.controller.ts](file://server\src\modules\favorites\favorites.controller.ts)
- [server\src\modules\favorites\favorites.service.ts](file://server\src\modules\favorites\favorites.service.ts)
- [server\src\models\index.ts](file://server\src\models\index.ts)
- [server\src\app.ts](file://server\src\app.ts)
- [my-uniapp-vue3\src\pages\favorites\index.vue](file://my-uniapp-vue3\src\pages\favorites\index.vue)
- [docs\API.md](file://docs\API.md)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件面向AI有声书生成平台的收藏功能，聚焦于收藏数据模型Favorite的设计与实现，涵盖字段定义、关系映射、业务逻辑、API使用示例以及前端展示与性能优化策略。收藏采用“用户-书籍”的多对多关系，通过唯一约束(userId, bookId)确保用户不能重复收藏同一本书，并提供收藏列表查询、单条收藏管理、收藏状态检查等能力。

## 项目结构
收藏功能涉及后端Prisma模型、控制器、服务层、数据库连接初始化以及前端收藏页面。整体结构如下：

```mermaid
graph TB
subgraph "后端"
APP["应用入口<br/>server/src/app.ts"]
CTRL["收藏控制器<br/>favorites.controller.ts"]
SVC["收藏服务<br/>favorites.service.ts"]
PRISMA["Prisma客户端<br/>server/src/models/index.ts"]
SCHEMA["数据模型定义<br/>server/prisma/schema.prisma"]
end
subgraph "前端"
PAGE["收藏页面<br/>my-uniapp-vue3/src/pages/favorites/index.vue"]
end
PAGE --> |"HTTP 请求"| APP
APP --> CTRL
CTRL --> SVC
SVC --> PRISMA
PRISMA --> SCHEMA
```

**图表来源**
- [server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)
- [server\src\modules\favorites\favorites.controller.ts:10-76](file://server\src\modules\favorites\favorites.controller.ts#L10-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server\src\modules\favorites\favorites.service.ts#L1-L104)
- [server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)
- [my-uniapp-vue3\src\pages\favorites\index.vue:47-112](file://my-uniapp-vue3\src\pages\favorites\index.vue#L47-L112)

**章节来源**
- [server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)

## 核心组件
- 收藏数据模型Favorite：定义收藏记录的字段、索引与关系。
- 收藏控制器FavoritesController：暴露收藏相关HTTP接口。
- 收藏服务FavoritesService：封装数据库操作，包含去重、查询、删除、检查等逻辑。
- 数据库连接初始化：Prisma客户端连接与生命周期管理。
- 前端收藏页面：展示收藏列表、执行收藏/取消收藏操作。

**章节来源**
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)
- [server\src\modules\favorites\favorites.controller.ts:10-76](file://server\src\modules\favorites\favorites.controller.ts#L10-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server\src\modules\favorites\favorites.service.ts#L1-L104)
- [server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)
- [my-uniapp-vue3\src\pages\favorites\index.vue:47-112](file://my-uniapp-vue3\src\pages\favorites\index.vue#L47-L112)

## 架构总览
收藏功能遵循典型的三层架构：前端页面发起HTTP请求，后端Koa路由分发至控制器，控制器调用服务层，服务层通过Prisma访问数据库。Prisma模型定义了User-Favorite-Book的关联关系与唯一约束。

```mermaid
sequenceDiagram
participant FE as "前端页面<br/>favorites/index.vue"
participant APP as "应用入口<br/>app.ts"
participant CTRL as "收藏控制器<br/>favorites.controller.ts"
participant SVC as "收藏服务<br/>favorites.service.ts"
participant DB as "Prisma客户端<br/>models/index.ts"
FE->>APP : GET /api/favorites
APP->>CTRL : 路由转发
CTRL->>SVC : getFavorites(userId)
SVC->>DB : 查询收藏并包含书籍信息
DB-->>SVC : 收藏列表
SVC-->>CTRL : 返回结果
CTRL-->>FE : JSON响应
FE->>APP : POST /api/favorites
APP->>CTRL : 路由转发
CTRL->>SVC : addFavorite(userId, audioId)
SVC->>DB : 去重检查 + 插入收藏
DB-->>SVC : 新收藏记录
SVC-->>CTRL : 返回结果
CTRL-->>FE : JSON响应
FE->>APP : DELETE /api/favorites/ : audioId
APP->>CTRL : 路由转发
CTRL->>SVC : removeFavorite(userId, audioId)
SVC->>DB : 删除收藏
DB-->>SVC : 删除结果
SVC-->>CTRL : 返回结果
CTRL-->>FE : JSON响应
```

**图表来源**
- [server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)
- [server\src\modules\favorites\favorites.controller.ts:10-76](file://server\src\modules\favorites\favorites.controller.ts#L10-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server\src\modules\favorites\favorites.service.ts#L1-L104)
- [server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)
- [my-uniapp-vue3\src\pages\favorites\index.vue:47-112](file://my-uniapp-vue3\src\pages\favorites\index.vue#L47-L112)

## 详细组件分析

### 收藏数据模型Favorite
- 字段定义
  - id：自增主键
  - userId：整型，外键指向User
  - bookId：整型，外键指向Book
  - createdAt：创建时间，默认当前时间
- 关系映射
  - 与User：一对多（User拥有多个收藏）
  - 与Book：一对多（Book被多个用户收藏）
- 唯一约束
  - 唯一索引：(userId, bookId)，防止重复收藏
- 索引
  - 单列索引：userId、bookId，提升查询与删除效率

```mermaid
erDiagram
USER {
int id PK
string phone UK
string openid UK
string nickname
string avatar
int memberLevel
datetime memberExpireAt
int dailyUsage
string lastUsageDate
datetime createdAt
datetime updatedAt
}
BOOK {
int id PK
int userId
string title
string subtitle
text description
string coverUrl
string targetAudience
string style
string bookScale
int totalChapters
int estimatedWords
int progress
boolean isPublished
longtext outlineJson
text foreword
text afterword
text errorMsg
datetime createdAt
datetime updatedAt
string failedStage
string genStage
text bookAnalysis
}
FAVORITE {
int id PK
int userId
int bookId
datetime createdAt
}
USER ||--o{ FAVORITE : "收藏"
BOOK ||--o{ FAVORITE : "被收藏"
```

**图表来源**
- [server\prisma\schema.prisma:10-38](file://server\prisma\schema.prisma#L10-L38)
- [server\prisma\schema.prisma:130-159](file://server\prisma\schema.prisma#L130-L159)
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)

**章节来源**
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)

### 收藏控制器与服务层
- 控制器职责
  - GET /api/favorites：获取当前用户的收藏列表
  - POST /api/favorites：添加收藏（传入audioId）
  - DELETE /api/favorites/:audioId：取消收藏
  - GET /api/favorites/check/:audioId：检查是否已收藏
  - 开发环境使用测试用户ID，生产环境通过optionalAuth中间件注入用户上下文
- 服务层职责
  - getFavorites(userId)：按用户查询收藏，包含书籍基本信息并按创建时间倒序
  - addFavorite(userId, bookId)：去重检查后插入收藏，返回包含书籍信息的收藏对象
  - removeFavorite(userId, bookId)：根据唯一键删除收藏
  - isFavorited(userId, bookId)：判断是否存在对应收藏

```mermaid
classDiagram
class FavoritesController {
+GET /favorites
+POST /favorites
+DELETE /favorites/ : audioId
+GET /favorites/check/ : audioId
}
class FavoritesService {
+getFavorites(userId)
+addFavorite(userId, bookId)
+removeFavorite(userId, bookId)
+isFavorited(userId, bookId)
}
class PrismaClient {
+favorite
+user
+book
}
FavoritesController --> FavoritesService : "调用"
FavoritesService --> PrismaClient : "查询/写入"
```

**图表来源**
- [server\src\modules\favorites\favorites.controller.ts:10-76](file://server\src\modules\favorites\favorites.controller.ts#L10-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server\src\modules\favorites\favorites.service.ts#L1-L104)
- [server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)

**章节来源**
- [server\src\modules\favorites\favorites.controller.ts:10-76](file://server\src\modules\favorites\favorites.controller.ts#L10-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server\src\modules\favorites\favorites.service.ts#L1-L104)

### 多对多关系设计与唯一约束
- 关系类型
  - 用户与收藏：一对多（一个用户可有多条收藏）
  - 收藏与书籍：一对多（一本书可被多个用户收藏）
- 唯一约束
  - (userId, bookId)确保同一用户对同一书籍只能收藏一次
- 查询优化
  - 为userId、bookId建立索引，支持快速定位与删除

```mermaid
flowchart TD
Start(["开始"]) --> CheckDup["检查是否存在 (userId, bookId)"]
CheckDup --> Exists{"已存在？"}
Exists --> |是| ReturnExisting["直接返回现有收藏"]
Exists --> |否| Insert["插入新收藏记录"]
Insert --> IncludeBook["包含书籍信息返回"]
ReturnExisting --> End(["结束"])
IncludeBook --> End
```

**图表来源**
- [server\src\modules\favorites\favorites.service.ts:34-69](file://server\src\modules\favorites\favorites.service.ts#L34-L69)

**章节来源**
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)
- [server\src\modules\favorites\favorites.service.ts:34-69](file://server\src\modules\favorites\favorites.service.ts#L34-L69)

### API使用示例
以下为常见操作的接口说明与调用路径（不包含具体代码内容）：
- 获取收藏列表
  - 方法与路径：GET /api/favorites
  - 认证：可选认证（optionalAuth）
  - 返回：收藏列表（包含书籍基本信息）
  - 参考：[server\src\modules\favorites\favorites.controller.ts:12-24](file://server\src\modules\favorites\favorites.controller.ts#L12-L24)
- 添加收藏
  - 方法与路径：POST /api/favorites
  - 请求体：{ audioId: number }
  - 认证：可选认证（optionalAuth）
  - 返回：新增或已存在的收藏（包含书籍信息）
  - 参考：[server\src\modules\favorites\favorites.controller.ts:26-44](file://server\src\modules\favorites\favorites.controller.ts#L26-L44)
- 取消收藏
  - 方法与路径：DELETE /api/favorites/:audioId
  - 认证：可选认证（optionalAuth）
  - 返回：成功消息
  - 参考：[server\src\modules\favorites\favorites.controller.ts:46-58](file://server\src\modules\favorites\favorites.controller.ts#L46-L58)
- 检查收藏状态
  - 方法与路径：GET /api/favorites/check/:audioId
  - 认证：可选认证（optionalAuth）
  - 返回：{ isFavorited: boolean }
  - 参考：[server\src\modules\favorites\favorites.controller.ts:60-73](file://server\src\modules\favorites\favorites.controller.ts#L60-L73)

此外，音频模块还提供了切换收藏的接口（PUT /api/audio/:id/favorite），用于在播放页等场景直接切换收藏状态，响应包含最新的收藏状态字段。参考：[docs\API.md:257-275](file://docs\API.md#L257-L275)

**章节来源**
- [server\src\modules\favorites\favorites.controller.ts:12-73](file://server\src\modules\favorites\favorites.controller.ts#L12-L73)
- [docs\API.md:257-275](file://docs\API.md#L257-L275)

### 前端展示与交互
- 页面布局
  - 顶部导航栏、加载状态、空状态提示、收藏列表项
- 数据绑定
  - 使用响应式数组渲染收藏列表
- 交互行为
  - 点击列表项进入播放页
  - 点击取消按钮触发删除请求并从列表移除
- 请求方法
  - GET /api/favorites 获取列表
  - DELETE /api/favorites/:audioId 取消收藏
  - 参考：[my-uniapp-vue3\src\pages\favorites\index.vue:47-112](file://my-uniapp-vue3\src\pages\favorites\index.vue#L47-L112)

```mermaid
sequenceDiagram
participant Page as "收藏页面<br/>favorites/index.vue"
participant API as "后端API"
Page->>API : GET /api/favorites
API-->>Page : 返回收藏列表
Page->>API : DELETE /api/favorites/ : audioId
API-->>Page : 返回成功
Page->>Page : 从本地列表移除该项
```

**图表来源**
- [my-uniapp-vue3\src\pages\favorites\index.vue:47-112](file://my-uniapp-vue3\src\pages\favorites\index.vue#L47-L112)
- [server\src\modules\favorites\favorites.controller.ts:46-58](file://server\src\modules\favorites\favorites.controller.ts#L46-L58)

**章节来源**
- [my-uniapp-vue3\src\pages\favorites\index.vue:47-112](file://my-uniapp-vue3\src\pages\favorites\index.vue#L47-L112)

## 依赖分析
- 组件耦合
  - 控制器依赖服务层；服务层依赖Prisma客户端；Prisma客户端依赖schema.prisma定义的模型
- 外部依赖
  - Koa路由、Prisma ORM、MySQL数据库
- 路由挂载
  - 应用入口集中挂载收藏路由，便于统一管理

```mermaid
graph LR
Controller["favorites.controller.ts"] --> Service["favorites.service.ts"]
Service --> Prisma["models/index.ts"]
Prisma --> Schema["prisma/schema.prisma"]
App["app.ts"] --> Controller
```

**图表来源**
- [server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)
- [server\src\modules\favorites\favorites.controller.ts:10-76](file://server\src\modules\favorites\favorites.controller.ts#L10-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server\src\modules\favorites\favorites.service.ts#L1-L104)
- [server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)

**章节来源**
- [server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)

## 性能考虑
- 查询优化
  - 为userId、bookId建立索引，加速收藏查询与删除
  - 收藏列表按创建时间倒序，避免全表扫描
- 去重与幂等
  - 插入前进行唯一键检查，避免重复收藏带来的写放大
- 前端缓存与分页
  - 前端可缓存收藏列表，减少重复请求
  - 若收藏量较大，建议后端提供分页参数（当前实现未显式分页，可按需扩展）
- 并发控制
  - 唯一键约束由数据库保证，避免竞态条件导致的重复收藏

[本节为通用性能建议，无需特定文件引用]

## 故障排查指南
- 常见问题
  - 重复收藏：由于唯一约束，重复收藏不会产生新记录，服务层会返回已存在记录
  - 权限问题：未登录时可选认证中间件允许访问，但需确认用户上下文是否正确注入
  - 数据库连接：若Prisma连接失败，检查数据库URL与网络连通性
- 排查步骤
  - 检查路由是否正确挂载至 /api/favorites
  - 确认Prisma客户端初始化与连接成功
  - 核对请求参数（如audioId）是否为空或类型错误
- 相关实现位置
  - 路由挂载：[server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)
  - 数据库连接：[server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)
  - 控制器参数校验：[server\src\modules\favorites\favorites.controller.ts:33-35](file://server\src\modules\favorites\favorites.controller.ts#L33-L35)

**章节来源**
- [server\src\app.ts:100-128](file://server\src\app.ts#L100-L128)
- [server\src\models\index.ts:1-15](file://server\src\models\index.ts#L1-L15)
- [server\src\modules\favorites\favorites.controller.ts:33-35](file://server\src\modules\favorites\favorites.controller.ts#L33-L35)

## 结论
收藏功能通过Prisma模型与服务层实现了清晰的数据关系与严格的去重约束，结合Koa路由与前端页面，形成了完整的收藏管理闭环。唯一约束确保数据一致性，索引与排序优化提升了查询性能。后续可在服务层增加分页与批量操作能力，进一步完善收藏管理体验。

[本节为总结性内容，无需特定文件引用]

## 附录
- 数据模型字段速览
  - Favorite：id、userId、bookId、createdAt
  - User：id、phone、openid、nickname、avatar、memberLevel、memberExpireAt、dailyUsage、lastUsageDate、createdAt、updatedAt
  - Book：id、userId、title、subtitle、description、coverUrl、targetAudience、style、bookScale、totalChapters、estimatedWords、progress、isPublished、outlineJson、foreword、afterword、errorMsg、createdAt、updatedAt、failedStage、genStage、bookAnalysis
- API一览
  - GET /api/favorites：获取收藏列表
  - POST /api/favorites：添加收藏
  - DELETE /api/favorites/:audioId：取消收藏
  - GET /api/favorites/check/:audioId：检查收藏状态
  - PUT /api/audio/:id/favorite：切换收藏状态（音频模块）

**章节来源**
- [server\prisma\schema.prisma:94-105](file://server\prisma\schema.prisma#L94-L105)
- [server\prisma\schema.prisma:10-38](file://server\prisma\schema.prisma#L10-L38)
- [server\prisma\schema.prisma:130-159](file://server\prisma\schema.prisma#L130-L159)
- [docs\API.md:257-275](file://docs\API.md#L257-L275)