# 收藏管理
**本文引用的文件**
- [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)
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 引言
本指南围绕“收藏管理”功能,系统性阐述后端控制器与服务层、数据库模型、前端页面与交互、以及API接口规范。重点覆盖以下能力:
- 收藏列表获取
- 添加收藏
- 取消收藏
- 收藏状态检查
并深入说明业务逻辑(用户身份验证、音频ID/书籍ID校验、重复收藏处理)、数据存储结构与查询优化策略,提供API接口文档与前端应用场景及用户交互设计,并给出常见问题与性能优化建议。
## 项目结构
收藏功能由三层组成:
- 前端页面:展示收藏列表、触发收藏/取消收藏、跳转播放页
- 后端控制器:接收请求、解析参数、调用服务层、返回统一响应
- 后端服务层:封装Prisma访问,执行查询/插入/删除/存在性检查
- 数据库模型:Favorite、Book、User三者关联,唯一索引避免重复收藏
```mermaid
graph TB
FE["前端页面
收藏列表页"] --> API["后端控制器
favorites.controller.ts"]
API --> SVC["服务层
favorites.service.ts"]
SVC --> PRISMA["Prisma 客户端"]
PRISMA --> DB["数据库
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 "控制器
favorites.controller.ts"
participant SVC as "服务层
favorites.service.ts"
participant DB as "数据库
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["鉴权中间件
auth.ts"] --> CTRL["控制器
favorites.controller.ts"]
CTRL --> SVC["服务层
favorites.service.ts"]
SVC --> PRISMA["Prisma 客户端"]
PRISMA --> DB["MySQL 数据库"]
FE["前端页面
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 (可选)
- 响应:code、message、data(收藏列表)
- 添加收藏
- 方法:POST
- URL:/favorites
- 请求头:Authorization: Bearer (可选)
- 请求体:{ audioId: number }
- 响应:code、message、data(收藏项)
- 取消收藏
- 方法:DELETE
- URL:/favorites/:audioId
- 请求头:Authorization: Bearer (可选)
- 响应:code、message
- 检查是否已收藏
- 方法:GET
- URL:/favorites/check/:audioId
- 请求头:Authorization: Bearer (可选)
- 响应: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)