# 历史记录

<cite>
**本文引用的文件**
- [server/src/modules/history/history.controller.ts](file://server/src/modules/history/history.controller.ts)
- [server/src/modules/history/history-batch.controller.ts](file://server/src/modules/history/history-batch.controller.ts)
- [server/src/modules/player/player.controller.ts](file://server/src/modules/player/player.controller.ts)
- [server/src/modules/player/player.service.ts](file://server/src/modules/player/player.service.ts)
- [server/src/modules/search/search.service.ts](file://server/src/modules/search/search.service.ts)
- [server/src/modules/search/search.controller.ts](file://server/src/modules/search/search.controller.ts)
- [server/prisma/schema.prisma](file://server/prisma/schema.prisma)
- [server/src/app.ts](file://server/src/app.ts)
- [server/src/middleware/auth.ts](file://server/src/middleware/auth.ts)
- [my-uniapp-vue3/src/pages/history/index.vue](file://my-uniapp-vue3/src/pages/history/index.vue)
- [my-uniapp-vue3/src/pages/search/index.vue](file://my-uniapp-vue3/src/pages/search/index.vue)
</cite>

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

## 简介
本技术文档围绕“历史记录”功能进行全面梳理，涵盖以下方面：
- 架构设计：播放历史追踪、搜索历史保存、批量历史操作
- 数据模型与存储策略：时间戳管理、去重机制、数据清理规则
- 查询优化与索引设计
- 历史记录API接口文档（含单条与批量）
- 用户体验：个性化推荐与内容发现
- 隐私保护与合规性

## 项目结构
历史记录相关能力横跨后端模块与前端页面：
- 后端
  - 历史记录模块：音频生成历史列表、批量删除
  - 播放历史模块：播放进度记录、最近播放
  - 搜索历史模块：历史查询、热门词、去重与清理
  - 数据模型：Prisma 定义的 AudioRecord、PlayRecord、SearchHistory 等
  - 应用入口：路由注册历史与播放、搜索模块
- 前端
  - 历史页面：分页加载、筛选（今日/本周/本月）、搜索、批量删除/下载
  - 搜索页面：历史与热门词展示

```mermaid
graph TB
subgraph "前端"
FE_History["历史页面<br/>my-uniapp-vue3/src/pages/history/index.vue"]
FE_Search["搜索页面<br/>my-uniapp-vue3/src/pages/search/index.vue"]
end
subgraph "后端"
APP["应用入口<br/>server/src/app.ts"]
AUTH["认证中间件<br/>server/src/middleware/auth.ts"]
subgraph "历史模块"
HIST_CTRL["历史控制器<br/>history.controller.ts"]
HIST_BATCH["批量删除控制器<br/>history-batch.controller.ts"]
end
subgraph "播放历史模块"
PLAYER_CTRL["播放控制器<br/>player.controller.ts"]
PLAYER_SRV["播放服务<br/>player.service.ts"]
end
subgraph "搜索历史模块"
SEARCH_CTRL["搜索控制器<br/>search.controller.ts"]
SEARCH_SRV["搜索服务<br/>search.service.ts"]
end
PRISMA["数据模型<br/>server/prisma/schema.prisma"]
end
FE_History --> |HTTP| HIST_CTRL
FE_History --> |HTTP| HIST_BATCH
FE_Search --> |HTTP| SEARCH_CTRL
FE_History --> |HTTP| PLAYER_CTRL
FE_History --> |HTTP| PLAYER_SRV
HIST_CTRL --> AUTH --> PRISMA
HIST_BATCH --> AUTH --> PRISMA
PLAYER_CTRL --> AUTH --> PRISMA
PLAYER_SRV --> PRISMA
SEARCH_CTRL --> AUTH --> PRISMA
SEARCH_SRV --> PRISMA
APP --> HIST_CTRL
APP --> HIST_BATCH
APP --> PLAYER_CTRL
APP --> SEARCH_CTRL
```

图表来源
- [server/src/app.ts:120-127](file://server/src/app.ts#L120-L127)
- [server/src/modules/history/history.controller.ts:1-67](file://server/src/modules/history/history.controller.ts#L1-L67)
- [server/src/modules/history/history-batch.controller.ts:1-105](file://server/src/modules/history/history-batch.controller.ts#L1-L105)
- [server/src/modules/player/player.controller.ts:1-344](file://server/src/modules/player/player.controller.ts#L1-L344)
- [server/src/modules/player/player.service.ts:1-280](file://server/src/modules/player/player.service.ts#L1-L280)
- [server/src/modules/search/search.controller.ts:69-169](file://server/src/modules/search/search.controller.ts#L69-L169)
- [server/src/modules/search/search.service.ts:1-144](file://server/src/modules/search/search.service.ts#L1-L144)
- [server/prisma/schema.prisma:220-373](file://server/prisma/schema.prisma#L220-L373)

章节来源
- [server/src/app.ts:120-127](file://server/src/app.ts#L120-L127)
- [server/src/modules/history/history.controller.ts:1-67](file://server/src/modules/history/history.controller.ts#L1-L67)
- [server/src/modules/history/history-batch.controller.ts:1-105](file://server/src/modules/history/history-batch.controller.ts#L1-L105)
- [server/src/modules/player/player.controller.ts:1-344](file://server/src/modules/player/player.controller.ts#L1-L344)
- [server/src/modules/player/player.service.ts:1-280](file://server/src/modules/player/player.service.ts#L1-L280)
- [server/src/modules/search/search.controller.ts:69-169](file://server/src/modules/search/search.controller.ts#L69-L169)
- [server/src/modules/search/search.service.ts:1-144](file://server/src/modules/search/search.service.ts#L1-L144)
- [server/prisma/schema.prisma:220-373](file://server/prisma/schema.prisma#L220-L373)

## 核心组件
- 历史记录控制器：提供历史列表查询，支持按起始时间过滤、分页与总数统计
- 批量删除控制器：提供批量删除音频生成历史的能力
- 播放历史控制器与服务：提供播放进度的增删改查、最近播放列表、章节音频合并等
- 搜索历史服务与控制器：提供搜索历史的保存、查询、清理与热门词统计
- 数据模型：AudioRecord、PlayRecord、SearchHistory 等，包含索引与字段约束
- 认证中间件：optionalAuth 支持未登录场景下的测试用户兜底

章节来源
- [server/src/modules/history/history.controller.ts:10-64](file://server/src/modules/history/history.controller.ts#L10-L64)
- [server/src/modules/history/history-batch.controller.ts:10-102](file://server/src/modules/history/history-batch.controller.ts#L10-L102)
- [server/src/modules/player/player.controller.ts:13-132](file://server/src/modules/player/player.controller.ts#L13-L132)
- [server/src/modules/player/player.service.ts:10-122](file://server/src/modules/player/player.service.ts#L10-L122)
- [server/src/modules/search/search.service.ts:57-119](file://server/src/modules/search/search.service.ts#L57-L119)
- [server/src/modules/search/search.controller.ts:69-169](file://server/src/modules/search/search.controller.ts#L69-L169)
- [server/prisma/schema.prisma:220-373](file://server/prisma/schema.prisma#L220-L373)
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)

## 架构总览
历史记录系统采用“模块化控制器 + 服务层 + Prisma ORM”的分层架构：
- 控制器负责请求解析、参数校验与响应封装
- 服务层封装业务逻辑（如去重、清理、合并）
- Prisma 模型定义数据结构与索引，保证查询效率与一致性
- optionalAuth 中间件在未登录时提供测试用户兜底，便于前端联调

```mermaid
sequenceDiagram
participant FE as "前端历史页面"
participant CTRL as "历史控制器"
participant AUTH as "optionalAuth"
participant PRISMA as "Prisma ORM"
FE->>CTRL : GET /api/history?page=&pageSize=&startDate=
CTRL->>AUTH : 校验可选认证
AUTH-->>CTRL : 设置用户上下文(测试用户或真实用户)
CTRL->>PRISMA : 查询 AudioRecord 列表+总数
PRISMA-->>CTRL : 返回记录与总数
CTRL-->>FE : 分页列表与分页信息
```

图表来源
- [server/src/modules/history/history.controller.ts:11-64](file://server/src/modules/history/history.controller.ts#L11-L64)
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

## 详细组件分析

### 历史记录模块（音频生成历史）
- 功能要点
  - 列表查询：支持按起始时间过滤、分页、总数统计
  - 字段映射：统一返回格式，包含标题、文本、音频链接、时长、字数、音色参数、状态、创建/更新时间等
- 关键实现
  - 控制器：分页查询与总数统计并行执行；where 条件根据 startDate 构建
  - 模型：AudioRecord 定义字段与索引（userId、audioId、bookId）

```mermaid
flowchart TD
Start(["进入历史列表接口"]) --> Parse["解析分页参数<br/>page/pageSize"]
Parse --> BuildWhere["构建查询条件<br/>startDate -> createdAt >= ..."]
BuildWhere --> Parallel["并行查询<br/>列表 + 总数"]
Parallel --> MapFields["字段映射与格式化"]
MapFields --> Return["返回分页数据"]
```

图表来源
- [server/src/modules/history/history.controller.ts:14-64](file://server/src/modules/history/history.controller.ts#L14-L64)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

章节来源
- [server/src/modules/history/history.controller.ts:10-64](file://server/src/modules/history/history.controller.ts#L10-L64)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

### 批量历史操作（批量删除）
- 功能要点
  - 支持 POST 与 DELETE 两种方式（兼容性考虑）
  - 校验 ids 数组合法性
  - 返回删除数量
- 关键实现
  - 控制器：解析请求体或查询参数，构造 in 查询条件，调用 Prisma deleteMany
  - 模型：基于 audioId 唯一键进行批量删除

```mermaid
sequenceDiagram
participant FE as "前端历史页面"
participant BATCH as "批量删除控制器"
participant AUTH as "optionalAuth"
participant PRISMA as "Prisma ORM"
FE->>BATCH : POST /api/history/batch-delete {ids[]}
BATCH->>AUTH : 校验可选认证
AUTH-->>BATCH : 设置用户上下文
BATCH->>BATCH : 校验ids数组
BATCH->>PRISMA : deleteMany({audioId : {in : ids}})
PRISMA-->>BATCH : 返回删除计数
BATCH-->>FE : {deletedCount}
```

图表来源
- [server/src/modules/history/history-batch.controller.ts:11-49](file://server/src/modules/history/history-batch.controller.ts#L11-L49)
- [server/src/modules/history/history-batch.controller.ts:52-102](file://server/src/modules/history/history-batch.controller.ts#L52-L102)
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

章节来源
- [server/src/modules/history/history-batch.controller.ts:10-102](file://server/src/modules/history/history-batch.controller.ts#L10-L102)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

### 播放历史模块（播放进度与最近播放）
- 功能要点
  - 保存/更新播放进度：upsert 语义，支持传入 duration
  - 删除单条播放记录
  - 获取最近播放列表（带进度百分比与书籍封面）
  - 章节音频合并：当播放章（level=1）时自动合并其下小节音频
- 关键实现
  - 控制器：提供 GET/POST/PUT/DELETE 接口
  - 服务层：封装 upsert、合并逻辑、最近播放查询
  - 模型：PlayRecord 唯一索引（userId, chapterId），支持关联章节与书籍

```mermaid
classDiagram
class PlayRecord {
+int id
+int userId
+int chapterId
+float progress
+float duration
+DateTime createdAt
+DateTime updatedAt
}
class BookChapter {
+int id
+int bookId
+int level
+int number
+String title
+String audioUrl
+int audioDuration
}
class Book {
+int id
+String title
+String coverUrl
}
PlayRecord --> BookChapter : "属于"
BookChapter --> Book : "属于"
```

图表来源
- [server/prisma/schema.prisma:63-77](file://server/prisma/schema.prisma#L63-L77)
- [server/prisma/schema.prisma:161-192](file://server/prisma/schema.prisma#L161-L192)
- [server/prisma/schema.prisma:130-159](file://server/prisma/schema.prisma#L130-L159)

章节来源
- [server/src/modules/player/player.controller.ts:13-132](file://server/src/modules/player/player.controller.ts#L13-L132)
- [server/src/modules/player/player.service.ts:10-122](file://server/src/modules/player/player.service.ts#L10-L122)
- [server/src/modules/player/player.service.ts:240-280](file://server/src/modules/player/player.service.ts#L240-L280)
- [server/prisma/schema.prisma:63-77](file://server/prisma/schema.prisma#L63-L77)
- [server/prisma/schema.prisma:161-192](file://server/prisma/schema.prisma#L161-L192)
- [server/prisma/schema.prisma:130-159](file://server/prisma/schema.prisma#L130-L159)

### 搜索历史模块（历史、热门、去重与清理）
- 功能要点
  - 保存搜索历史：去重（同一用户相同关键词仅保留最新）、限制最大条数（超过则删除最旧）
  - 查询历史：按时间倒序，distinct 关键词
  - 清理历史：单条删除、清空全部
  - 热门词统计：每次保存时增加计数，不存在则新建
- 关键实现
  - 服务层：删除旧同关键词记录、限制最大长度、更新热门词
  - 控制器：参数校验、异常捕获
  - 模型：SearchHistory、HotSearch

```mermaid
flowchart TD
Save["保存搜索历史"] --> Dedup["删除同用户同关键词旧记录"]
Dedup --> InsertNew["插入新记录"]
InsertNew --> Limit["查询用户历史并限制数量<=20"]
Limit --> Cleanup["删除超出部分"]
Cleanup --> UpdateHot["更新/新增热门词计数"]
UpdateHot --> Done["完成"]
```

图表来源
- [server/src/modules/search/search.service.ts:72-119](file://server/src/modules/search/search.service.ts#L72-L119)
- [server/prisma/schema.prisma:220-240](file://server/prisma/schema.prisma#L220-L240)

章节来源
- [server/src/modules/search/search.service.ts:57-119](file://server/src/modules/search/search.service.ts#L57-L119)
- [server/src/modules/search/search.controller.ts:69-169](file://server/src/modules/search/search.controller.ts#L69-L169)
- [server/prisma/schema.prisma:220-240](file://server/prisma/schema.prisma#L220-L240)

### 前端集成与用户体验
- 历史页面
  - 支持标签筛选（全部/今天/本周/本月），按 startDate 过滤
  - 支持搜索关键词过滤
  - 支持批量删除与批量下载
  - 分页加载与骨架屏
- 搜索页面
  - 展示搜索历史与热门词，支持点击回填

章节来源
- [my-uniapp-vue3/src/pages/history/index.vue:168-360](file://my-uniapp-vue3/src/pages/history/index.vue#L168-L360)
- [my-uniapp-vue3/src/pages/history/index.vue:202-291](file://my-uniapp-vue3/src/pages/history/index.vue#L202-L291)
- [my-uniapp-vue3/src/pages/search/index.vue:166-191](file://my-uniapp-vue3/src/pages/search/index.vue#L166-L191)

## 依赖关系分析
- 路由注册
  - 历史模块：/api/history
  - 播放历史模块：/api/player
  - 搜索模块：/api/search
- 认证依赖
  - optionalAuth 为历史与播放等模块提供可选认证，未登录时使用测试用户
- 数据模型依赖
  - AudioRecord、PlayRecord、SearchHistory 等模型定义字段与索引，支撑查询与去重

```mermaid
graph LR
APP["app.ts"] --> HIST["/api/history"]
APP --> PLAYER["/api/player"]
APP --> SEARCH["/api/search"]
HIST --> AUTH["optionalAuth"]
PLAYER --> AUTH
SEARCH --> AUTH
HIST --> PRISMA["Prisma模型"]
PLAYER --> PRISMA
SEARCH --> PRISMA
```

图表来源
- [server/src/app.ts:120-127](file://server/src/app.ts#L120-L127)
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

章节来源
- [server/src/app.ts:120-127](file://server/src/app.ts#L120-L127)
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

## 性能考量
- 查询优化
  - 历史列表：按 createdAt 倒序分页，使用 where 条件过滤；并行查询列表与总数
  - 播放历史：唯一索引（userId, chapterId）支持快速 upsert 与查询
  - 搜索历史：对 userId、keyword 建立索引，distinct 关键词减少重复
- 索引设计
  - AudioRecord：userId、audioId、bookId
  - PlayRecord：userId、chapterId（唯一）
  - SearchHistory：userId、keyword、createdAt
- 批量操作
  - 批量删除使用 deleteMany，避免循环逐条删除
- 前端分页
  - 前端按需加载，避免一次性拉取大量历史数据

章节来源
- [server/src/modules/history/history.controller.ts:23-31](file://server/src/modules/history/history.controller.ts#L23-L31)
- [server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)
- [server/prisma/schema.prisma:63-77](file://server/prisma/schema.prisma#L63-L77)
- [server/prisma/schema.prisma:220-228](file://server/prisma/schema.prisma#L220-L228)

## 故障排查指南
- 认证相关
  - optionalAuth 在未携带有效 Token 时会使用测试用户兜底；若出现数据归属异常，检查前端是否正确传递 Authorization
- 历史列表为空
  - 确认 startDate 是否导致过滤范围过小；检查分页参数 page/pageSize
- 批量删除失败
  - 确认 ids 数组格式与长度；查看控制器错误处理返回
- 播放历史异常
  - 检查 userId 类型转换；确认唯一索引是否存在重复冲突
- 搜索历史未去重
  - 确认服务层是否先删除同用户同关键词旧记录再插入新记录

章节来源
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server/src/modules/history/history.controller.ts:14-21](file://server/src/modules/history/history.controller.ts#L14-L21)
- [server/src/modules/history/history-batch.controller.ts:17-24](file://server/src/modules/history/history-batch.controller.ts#L17-L24)
- [server/src/modules/player/player.service.ts:46-81](file://server/src/modules/player/player.service.ts#L46-L81)
- [server/src/modules/search/search.service.ts:77-101](file://server/src/modules/search/search.service.ts#L77-L101)

## 结论
历史记录系统通过清晰的模块划分与合理的数据模型设计，实现了播放历史、搜索历史与批量操作的完整闭环。配合 optionalAuth 的可选认证与前端的友好交互，既满足开发调试需求，也保障了生产可用性。后续可在热点数据缓存、冷数据归档与更细粒度的隐私控制上进一步优化。

## 附录

### 历史记录API接口文档

- 获取历史列表
  - 方法：GET
  - 路径：/api/history
  - 认证：可选
  - 查询参数：
    - page：页码（默认 1）
    - pageSize：每页条数（默认 20）
    - startDate：起始时间（ISO8601，用于 createdAt >= ...）
  - 响应：分页列表与总数、页码信息
  - 示例路径：[server/src/modules/history/history.controller.ts:11-64](file://server/src/modules/history/history.controller.ts#L11-L64)

- 批量删除历史
  - 方法：POST
  - 路径：/api/history/batch-delete
  - 认证：可选
  - 请求体：
    - ids：字符串数组（音频 ID 列表）
  - 响应：删除数量
  - 示例路径：[server/src/modules/history/history-batch.controller.ts:11-49](file://server/src/modules/history/history-batch.controller.ts#L11-L49)

- 兼容删除（保留 DELETE）
  - 方法：DELETE
  - 路径：/api/history/batch
  - 认证：可选
  - 请求体或查询参数：
    - ids：字符串数组（音频 ID 列表）
  - 响应：删除数量
  - 示例路径：[server/src/modules/history/history-batch.controller.ts:52-102](file://server/src/modules/history/history-batch.controller.ts#L52-L102)

- 播放历史相关接口
  - 获取播放进度列表：GET /api/player/progress?audioId=...
  - 保存播放进度：POST /api/player/progress
  - 更新播放进度：PUT /api/player/progress/:audioId
  - 删除播放记录：DELETE /api/player/progress/:audioId
  - 批量删除播放记录：DELETE /api/player/progress/batch
  - 最近播放列表：GET /api/player/recent
  - 示例路径：
    - [server/src/modules/player/player.controller.ts:13-132](file://server/src/modules/player/player.controller.ts#L13-L132)
    - [server/src/modules/player/player.controller.ts:90-107](file://server/src/modules/player/player.controller.ts#L90-L107)

- 搜索历史相关接口
  - 获取搜索历史：GET /api/search/history?userId=&limit=
  - 保存搜索历史：POST /api/search/history
  - 删除单条历史：DELETE /api/search/history?userId=&keyword=
  - 清空历史：DELETE /api/search/history/all?userId=
  - 示例路径：
    - [server/src/modules/search/search.controller.ts:69-169](file://server/src/modules/search/search.controller.ts#L69-L169)

### 数据模型与索引
- AudioRecord
  - 关键字段：userId、audioId（唯一）、title、text、wordCount、voiceId、voiceParams、audioUrl、audioDuration、audioSize、status、errorMsg、createdAt、updatedAt、bookId
  - 索引：userId、audioId、bookId
  - 示例路径：[server/prisma/schema.prisma:352-373](file://server/prisma/schema.prisma#L352-L373)

- PlayRecord
  - 关键字段：userId、chapterId、progress、duration、createdAt、updatedAt
  - 索引：userId、chapterId（唯一）
  - 示例路径：[server/prisma/schema.prisma:63-77](file://server/prisma/schema.prisma#L63-L77)

- SearchHistory
  - 关键字段：userId、keyword、createdAt
  - 索引：userId、userId+createdAt
  - 示例路径：[server/prisma/schema.prisma:220-228](file://server/prisma/schema.prisma#L220-L228)

- HotSearch
  - 关键字段：keyword、count、sort、createdAt、updatedAt
  - 索引：sort、count
  - 示例路径：[server/prisma/schema.prisma:230-240](file://server/prisma/schema.prisma#L230-L240)

### 用户体验与个性化
- 历史页面支持按时间段筛选与关键词搜索，提升内容发现效率
- 播放历史支持最近播放列表，便于继续播放
- 搜索历史去重与热门词统计，辅助个性化推荐与搜索引导

章节来源
- [my-uniapp-vue3/src/pages/history/index.vue:168-360](file://my-uniapp-vue3/src/pages/history/index.vue#L168-L360)
- [server/src/modules/player/player.controller.ts:111-132](file://server/src/modules/player/player.controller.ts#L111-L132)
- [server/src/modules/search/search.service.ts:57-119](file://server/src/modules/search/search.service.ts#L57-L119)

### 隐私保护与合规性
- 可选认证：optionalAuth 在未登录时使用测试用户，避免真实用户数据泄露
- 历史清理：搜索历史限制最大条数并定期清理，降低数据留存风险
- 建议补充：
  - 明确历史数据的保留期限与删除策略
  - 对敏感字段（如 text）进行脱敏或最小化采集
  - 提供用户自助导出/删除历史的功能入口

章节来源
- [server/src/middleware/auth.ts:51-80](file://server/src/middleware/auth.ts#L51-L80)
- [server/src/modules/search/search.service.ts:90-101](file://server/src/modules/search/search.service.ts#L90-L101)