# 收藏管理

<cite>
**本文引用的文件**
- [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\middleware\auth.ts](file://server/src/middleware/auth.ts)
- [server\prisma\schema.prisma](file://server/prisma/schema.prisma)
- [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. [附录](#附录)

## 引言
本指南围绕“收藏管理”功能，系统性阐述后端控制器与服务层、数据库模型、前端页面与交互、以及API接口规范。重点覆盖以下能力：
- 收藏列表获取
- 添加收藏
- 取消收藏
- 收藏状态检查
并深入说明业务逻辑（用户身份验证、音频ID/书籍ID校验、重复收藏处理）、数据存储结构与查询优化策略，提供API接口文档与前端应用场景及用户交互设计，并给出常见问题与性能优化建议。

## 项目结构
收藏功能由三层组成：
- 前端页面：展示收藏列表、触发收藏/取消收藏、跳转播放页
- 后端控制器：接收请求、解析参数、调用服务层、返回统一响应
- 后端服务层：封装Prisma访问，执行查询/插入/删除/存在性检查
- 数据库模型：Favorite、Book、User三者关联，唯一索引避免重复收藏

```mermaid
graph TB
FE["前端页面<br/>收藏列表页"] --> API["后端控制器<br/>favorites.controller.ts"]
API --> SVC["服务层<br/>favorites.service.ts"]
SVC --> PRISMA["Prisma 客户端"]
PRISMA --> DB["数据库<br/>schema.prisma 中的 Favorite/Book/User"]
```

图表来源
- [server\src\modules\favorites\favorites.controller.ts:1-76](file://server/src/modules/favorites/favorites.controller.ts#L1-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server/src/modules/favorites/favorites.service.ts#L1-L104)
- [server\prisma\schema.prisma:94-105](file://server/prisma/schema.prisma#L94-L105)

章节来源
- [server\src\modules\favorites\favorites.controller.ts:1-76](file://server/src/modules/favorites/favorites.controller.ts#L1-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server/src/modules/favorites/favorites.service.ts#L1-L104)
- [server\prisma\schema.prisma:94-105](file://server/prisma/schema.prisma#L94-L105)

## 核心组件
- 控制器层：定义路由、参数解析、鉴权中间件、统一响应格式
- 服务层：封装Prisma查询，处理重复收藏、排序与关联查询
- 鉴权中间件：可选认证，开发环境默认测试用户
- 数据模型：Favorite唯一约束、索引优化、与Book/User关联

章节来源
- [server\src\modules\favorites\favorites.controller.ts:1-76](file://server/src/modules/favorites/favorites.controller.ts#L1-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server/src/modules/favorites/favorites.service.ts#L1-L104)
- [server\src\middleware\auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server\prisma\schema.prisma:94-105](file://server/prisma/schema.prisma#L94-L105)

## 架构总览
收藏功能遵循“控制器-服务-数据模型”的分层架构，前端通过HTTP请求与后端交互，后端通过Prisma访问MySQL数据库。

```mermaid
sequenceDiagram
participant U as "用户"
participant FE as "前端页面"
participant CTRL as "控制器<br/>favorites.controller.ts"
participant SVC as "服务层<br/>favorites.service.ts"
participant DB as "数据库<br/>Prisma/MySQL"
U->>FE : 打开收藏页面
FE->>CTRL : GET /favorites
CTRL->>SVC : getFavorites(userId)
SVC->>DB : 查询收藏记录(含book关联)
DB-->>SVC : 收藏列表
SVC-->>CTRL : 返回结果
CTRL-->>FE : 统一响应
U->>FE : 点击添加收藏
FE->>CTRL : POST /favorites {audioId}
CTRL->>SVC : addFavorite(userId, bookId)
SVC->>DB : 检查重复(唯一索引)
alt 已存在
DB-->>SVC : 存在
SVC-->>CTRL : 返回现有收藏
else 不存在
DB-->>SVC : 插入新收藏
SVC-->>CTRL : 返回新建收藏
end
CTRL-->>FE : 统一响应
U->>FE : 点击取消收藏
FE->>CTRL : DELETE /favorites/ : audioId
CTRL->>SVC : removeFavorite(userId, bookId)
SVC->>DB : 删除收藏
DB-->>SVC : 成功
SVC-->>CTRL : 返回
CTRL-->>FE : 统一响应
U->>FE : 检查收藏状态
FE->>CTRL : GET /favorites/check/ : audioId
CTRL->>SVC : isFavorited(userId, bookId)
SVC->>DB : 查找唯一键
DB-->>SVC : 存在/不存在
SVC-->>CTRL : 返回布尔值
CTRL-->>FE : 统一响应
```

图表来源
- [server\src\modules\favorites\favorites.controller.ts:13-73](file://server/src/modules/favorites/favorites.controller.ts#L13-L73)
- [server\src\modules\favorites\favorites.service.ts:8-103](file://server/src/modules/favorites/favorites.service.ts#L8-L103)
- [server\prisma\schema.prisma:94-105](file://server/prisma/schema.prisma#L94-L105)

## 详细组件分析

### 控制器层（favorites.controller.ts）
- 路由定义
  - GET /favorites：获取当前用户的收藏列表
  - POST /favorites：添加收藏（请求体包含audioId）
  - DELETE /favorites/:audioId：取消收藏
  - GET /favorites/check/:audioId：检查是否已收藏
- 鉴权策略
  - 使用可选认证中间件，支持无Token时使用测试用户
  - 开发环境可通过环境变量控制是否强制认证
- 统一响应
  - 固定返回结构：code、message、data
  - 错误通过错误处理器抛出，由全局中间件捕获

章节来源
- [server\src\modules\favorites\favorites.controller.ts:13-73](file://server/src/modules/favorites/favorites.controller.ts#L13-L73)
- [server\src\middleware\auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)

### 服务层（favorites.service.ts）
- 数据访问
  - getFavorites：按用户ID查询收藏，包含book关联字段，按创建时间倒序
  - addFavorite：先查重（唯一索引），存在则返回现有；否则创建新收藏并返回带book的完整信息
  - removeFavorite：按唯一键删除收藏
  - isFavorited：按唯一键判断是否存在
- 关联查询
  - 通过Prisma include book，减少二次查询
- 性能要点
  - 唯一键约束避免重复收藏
  - 查询使用索引字段（userId、bookId）

章节来源
- [server\src\modules\favorites\favorites.service.ts:8-103](file://server/src/modules/favorites/favorites.service.ts#L8-L103)
- [server\prisma\schema.prisma:94-105](file://server/prisma/schema.prisma#L94-L105)

### 数据模型（schema.prisma）
- Favorite
  - 唯一键：userId + bookId
  - 索引：userId、bookId
  - 关系：属于User、属于Book（级联删除）
- Book
  - 收藏反向关系：favorites
- User
  - 收藏反向关系：favorites

```mermaid
erDiagram
USER ||--o{ FAVORITE : "拥有"
BOOK ||--o{ FAVORITE : "被收藏"
FAVORITE {
int id PK
int userId
int bookId
datetime createdAt
}
USER {
int id PK
}
BOOK {
int id PK
}
```

图表来源
- [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)

### 前端应用（favorites/index.vue）
- 页面职责
  - 加载收藏列表、空状态提示、点击播放、点击取消收藏
- 交互流程
  - 打开页面即拉取收藏列表
  - 点击条目跳转播放页
  - 点击右侧爱心图标触发取消收藏，成功后从本地列表剔除
- 类型定义
  - FavoriteItem：包含收藏项ID、音频ID、创建时间、音频信息等

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

## 依赖关系分析
- 控制器依赖服务层
- 服务层依赖Prisma客户端访问数据库
- 鉴权中间件贯穿控制器层
- 前端依赖统一的HTTP请求工具与后端API

```mermaid
graph LR
AUTH["鉴权中间件<br/>auth.ts"] --> CTRL["控制器<br/>favorites.controller.ts"]
CTRL --> SVC["服务层<br/>favorites.service.ts"]
SVC --> PRISMA["Prisma 客户端"]
PRISMA --> DB["MySQL 数据库"]
FE["前端页面<br/>favorites/index.vue"] --> CTRL
```

图表来源
- [server\src\middleware\auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server\src\modules\favorites\favorites.controller.ts:1-76](file://server/src/modules/favorites/favorites.controller.ts#L1-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server/src/modules/favorites/favorites.service.ts#L1-L104)

章节来源
- [server\src\middleware\auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server\src\modules\favorites\favorites.controller.ts:1-76](file://server/src/modules/favorites/favorites.controller.ts#L1-L76)
- [server\src\modules\favorites\favorites.service.ts:1-104](file://server/src/modules\favorites/favorites.service.ts#L1-L104)

## 性能考量
- 查询优化
  - 使用唯一索引(userId, bookId)保证重复收藏检查高效
  - 对userId、bookId建立索引，提升查询与删除效率
- 关联查询
  - 通过include book一次性返回所需字段，避免N+1查询
- 排序与分页
  - 按创建时间倒序，适合“最近收藏”展示
  - 如需分页，可在服务层增加skip/take参数
- 缓存策略
  - 对热门用户收藏可考虑Redis缓存，降低数据库压力
- 并发控制
  - 唯一键约束天然防止并发重复插入
- 日志与监控
  - 记录慢查询与异常，结合数据库慢日志定位瓶颈

## 故障排查指南
- 常见问题
  - 未登录或Token无效：确认鉴权中间件配置与Token格式
  - 收藏重复：唯一键约束会阻止重复，检查是否正确处理已存在情况
  - 取消收藏失败：确认传入的audioId是否正确、用户是否匹配
  - 收藏列表为空：确认用户是否有收藏记录、include book是否正常
- 排查步骤
  - 检查控制器参数解析与类型转换
  - 在服务层打印SQL与参数，核对userId与bookId
  - 核对Prisma schema中的唯一键与索引
  - 前端确认请求URL与请求头（Authorization）
- 错误处理
  - 控制器层统一抛出错误，响应格式固定
  - 前端根据code与message进行提示

章节来源
- [server\src\modules\favorites\favorites.controller.ts:33-35](file://server/src/modules/favorites/favorites.controller.ts#L33-L35)
- [server\src\modules\favorites\favorites.service.ts:37-49](file://server/src/modules\favorites/favorites.service.ts#L37-L49)
- [server\src\middleware\auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)

## 结论
收藏管理功能采用清晰的分层架构与完善的数据库约束，实现了稳定的收藏列表、添加、取消与状态检查能力。通过唯一键与索引优化、关联查询与统一响应，兼顾了易用性与性能。前端页面简洁直观，配合后端API即可快速落地。

## 附录

### API接口文档（收藏相关）
- 获取收藏列表
  - 方法：GET
  - URL：/favorites
  - 请求头：Authorization: Bearer <token>（可选）
  - 响应：code、message、data（收藏列表）
- 添加收藏
  - 方法：POST
  - URL：/favorites
  - 请求头：Authorization: Bearer <token>（可选）
  - 请求体：{ audioId: number }
  - 响应：code、message、data（收藏项）
- 取消收藏
  - 方法：DELETE
  - URL：/favorites/:audioId
  - 请求头：Authorization: Bearer <token>（可选）
  - 响应：code、message
- 检查是否已收藏
  - 方法：GET
  - URL：/favorites/check/:audioId
  - 请求头：Authorization: Bearer <token>（可选）
  - 响应：code、message、data: { isFavorited: boolean }

章节来源
- [server\src\modules\favorites\favorites.controller.ts:13-73](file://server/src/modules/favorites/favorites.controller.ts#L13-L73)
- [docs\API.md:257-276](file://docs/API.md#L257-L276)

### 数据模型与查询流程图
```mermaid
flowchart TD
Start(["进入收藏服务"]) --> Parse["解析用户ID与音频ID"]
Parse --> CheckDup{"是否已收藏？"}
CheckDup --> |是| ReturnExist["返回现有收藏"]
CheckDup --> |否| Insert["创建新收藏"]
Insert --> ReturnNew["返回新建收藏"]
ReturnExist --> End(["结束"])
ReturnNew --> End
```

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