1090 lines
34 KiB
Markdown
1090 lines
34 KiB
Markdown
# 阅读打卡小程序 - 产品需求文档 (PRD)
|
||
|
||
## 1. 产品概述
|
||
|
||
### 1.1 产品定位
|
||
一款阅读打卡小程序,通过 AI 预生成每本书的分日阅读计划(含导读、预估阅读时长、理解问题),引导用户按计划阅读并每日打卡,培养持续阅读习惯。
|
||
|
||
### 1.2 核心价值
|
||
- **AI 阅读计划**:AI 预先为每本书拆分每日阅读导读,预估阅读时长,后台可人工调整
|
||
- **引导式阅读**:每天展示导读内容 + 预估阅读时长 + 理解问题,降低阅读门槛
|
||
- **打卡激励**:读完当日内容后回答问题、写读书感悟,完成打卡
|
||
- **分享传播**:生成精美分享宣传图,展示阅读成果
|
||
|
||
### 1.3 目标用户
|
||
- 想养成阅读习惯但缺乏计划性的用户
|
||
- 读书会/学习社群成员
|
||
- 家长引导孩子阅读
|
||
|
||
---
|
||
|
||
## 2. 功能范围
|
||
|
||
### 2.1 核心功能(MVP)
|
||
|
||
#### ✅ 书单与书籍
|
||
- 浏览书单列表(按分类/主题组织)
|
||
- 查看书单下的书籍列表
|
||
- 查看书籍详情和阅读计划概览
|
||
|
||
#### ✅ AI 阅读计划
|
||
- AI 预生成每本书的分日阅读导读(后台触发)
|
||
- 每日导读包含:导读内容、阅读范围(页码/章节)、**AI 预估阅读时长**、理解问题
|
||
- 后台管理员可编辑调整 AI 生成的计划内容
|
||
|
||
#### ✅ 每日打卡
|
||
- 展示今日导读内容和 AI 预估阅读时长(用户无需手动填写时长)
|
||
- 用户阅读完成后回答理解问题
|
||
- 提交打卡 + 选填读书感悟
|
||
- 打卡日历展示连续打卡记录
|
||
|
||
#### ✅ 分享宣传图
|
||
- 生成分享海报(连续打卡天数、已读书籍数、累计阅读时长等)
|
||
- 支持多种海报模板
|
||
|
||
#### ✅ 阅读统计
|
||
- 连续打卡天数 / 最长连续天数
|
||
- 已读完书籍数
|
||
- 累计阅读时长(由 AI 预估时长按打卡天数累加)
|
||
- 阅读趋势图
|
||
|
||
### 2.2 暂不实现(后续迭代)
|
||
- ❌ 用户自建书单
|
||
- ❌ 社区互动(评论/点赞感悟)
|
||
- ❌ 阅读排行榜
|
||
- ❌ 订阅消息推送提醒
|
||
- ❌ 书籍推荐算法
|
||
|
||
---
|
||
|
||
## 3. 页面结构
|
||
|
||
### 3.1 页面清单与 TabBar
|
||
|
||
```
|
||
TabBar:
|
||
├── 今日阅读 (pages/home/index) - 首页,展示今日任务与打卡入口
|
||
├── 书单 (pages/book-lists/index) - 浏览书单和书籍
|
||
├── 统计 (pages/stats/index) - 阅读数据统计
|
||
└── 我的 (pages/profile/index) - 个人中心
|
||
|
||
非 Tab 页:
|
||
├── 书籍详情 (pages/book-detail/index) - 书籍信息 + 阅读计划预览
|
||
├── 阅读打卡 (pages/reading/index) - 今日导读 + 问题 + 打卡
|
||
├── 感悟列表 (pages/reflections/index) - 某本书的感悟记录
|
||
└── 分享海报 (pages/poster/index) - 生成/预览分享图
|
||
```
|
||
|
||
### 3.2 首页 — 今日阅读 (home)
|
||
|
||
**核心目标**:一目了然展示今日阅读任务,快速进入打卡
|
||
|
||
| 元素 | 说明 | 数据来源 |
|
||
|------|------|----------|
|
||
| 打卡日历条 | 近 7 天打卡状态(圆点标记) | `GET /reading/my/stats` |
|
||
| 在读书籍卡片 | 书名、封面、进度(第 X/Y 天) | `GET /reading/my/books` |
|
||
| 今日任务预览 | 导读标题 + AI 预估阅读时长 | `GET /reading/my/books/:id/today` |
|
||
| 打卡按钮 | "开始今日阅读" / "今日已打卡 ✅" | 根据 daily_status 判断 |
|
||
| 空状态引导 | 未选书时展示 "去选一本书开始吧" | 跳转书单页 |
|
||
|
||
**首页布局示意**:
|
||
|
||
```
|
||
┌─────────────────────────────┐
|
||
│ 📖 今日阅读 │
|
||
│ ───────────────────────── │
|
||
│ [日 一 二 三 四 五 六] │
|
||
│ ● ● ● ○ ○ ○ ○ │
|
||
│ 连续打卡 3 天 │
|
||
│ ───────────────────────── │
|
||
│ ┌───────────────────────┐ │
|
||
│ │ 📕 《人类简史》 │ │
|
||
│ │ 第 5/30 天 │ │
|
||
│ │ ████████░░░ 17% │ │
|
||
│ └───────────────────────┘ │
|
||
│ ───────────────────────── │
|
||
│ 今日导读:第三章 农业革命 │
|
||
│ ⏱ 预估阅读 25 分钟 │
|
||
│ 📝 3 道理解问题 │
|
||
│ ───────────────────────── │
|
||
│ [ 🟢 开始今日阅读 ] │
|
||
└─────────────────────────────┘
|
||
```
|
||
|
||
### 3.3 书单页 (book-lists)
|
||
|
||
**核心目标**:浏览和选择想读的书
|
||
|
||
| 元素 | 说明 |
|
||
|------|------|
|
||
| 书单卡片列表 | 书单封面、标题、描述、书籍数量 |
|
||
| 点击书单 | 展开书单下的书籍列表 |
|
||
| 书籍卡片 | 书名、作者、封面、计划天数、"开始阅读"按钮 |
|
||
|
||
### 3.4 书籍详情页 (book-detail)
|
||
|
||
**核心目标**:了解书籍信息和阅读计划,决定是否开始阅读
|
||
|
||
| 元素 | 说明 |
|
||
|------|------|
|
||
| 书籍封面大图 | 封面、书名、作者 |
|
||
| 书籍简介 | 内容简介 |
|
||
| 阅读计划概览 | 共 X 天、AI 预估总阅读时长 |
|
||
| 每日计划预览 | 可展开查看每天的导读标题和预估时长 |
|
||
| 操作按钮 | "开始阅读"(未开始)/ "继续阅读"(进行中)/ "已读完"(已完成) |
|
||
|
||
### 3.5 阅读打卡页 (reading)
|
||
|
||
**核心目标**:完成今日阅读任务并打卡
|
||
|
||
```
|
||
┌─────────────────────────────┐
|
||
│ ← 返回 第 5/30 天 │
|
||
│ ───────────────────────── │
|
||
│ 📖 今日导读 │
|
||
│ 第三章 农业革命 (P45-P62) │
|
||
│ ───────────────────────── │
|
||
│ ⏱ 预估阅读时长: 25 分钟 │
|
||
│ ───────────────────────── │
|
||
│ [导读内容区域] │
|
||
│ "农业革命是人类历史上最具 │
|
||
│ 争议的转折点之一。作者认为 │
|
||
│ 农业并非进步,而是..." │
|
||
│ ───────────────────────── │
|
||
│ 📝 阅读理解 (读完后作答) │
|
||
│ ───────────────────────── │
|
||
│ Q1: 作者为什么认为农业革命 │
|
||
│ 是"史上最大的骗局"? │
|
||
│ [文本输入框] │
|
||
│ │
|
||
│ Q2: 采集社会与农业社会相比 │
|
||
│ 各有什么优劣? │
|
||
│ [文本输入框] │
|
||
│ │
|
||
│ Q3: 你是否同意作者的观点? │
|
||
│ 为什么? │
|
||
│ [文本输入框] │
|
||
│ ───────────────────────── │
|
||
│ 💭 读书感悟 (选填) │
|
||
│ [多行文本输入框] │
|
||
│ ───────────────────────── │
|
||
│ [ ✅ 完成今日打卡 ] │
|
||
└─────────────────────────────┘
|
||
```
|
||
|
||
**交互规则**:
|
||
- 理解问题至少回答 1 道才能提交打卡
|
||
- 读书感悟为选填项
|
||
- 打卡成功后展示鼓励动画 + 连续打卡天数
|
||
- 当天最后一天打卡 → 弹出"恭喜读完全书" + 引导生成分享图
|
||
|
||
### 3.6 统计页 (stats)
|
||
|
||
| 元素 | 说明 |
|
||
|------|------|
|
||
| 核心数据卡片 | 连续打卡天数、已读完书籍数、累计阅读时长 |
|
||
| 打卡日历 | 月度日历视图,标记已打卡日期 |
|
||
| 阅读趋势 | 周/月维度的每日阅读时长趋势图(来自 AI 预估时长) |
|
||
| 已读书籍列表 | 已完成的书籍及完成日期 |
|
||
|
||
### 3.7 个人中心 (profile)
|
||
|
||
| 元素 | 说明 |
|
||
|------|------|
|
||
| 用户信息 | 头像、昵称 |
|
||
| 在读书籍 | 当前正在阅读的书籍列表 |
|
||
| 已读书籍 | 已完成的书籍列表 |
|
||
| 我的感悟 | 所有读书感悟汇总 |
|
||
| 分享成就 | 进入海报生成页 |
|
||
|
||
### 3.8 分享海报页 (poster)
|
||
|
||
| 元素 | 说明 |
|
||
|------|------|
|
||
| 海报预览 | 实时预览生成的宣传图 |
|
||
| 模板选择 | 多种海报风格可选 |
|
||
| 展示字段 | 连续打卡天数、已读书籍数、累计阅读时长、当前在读书名 |
|
||
| 操作按钮 | "保存到相册" / "分享给好友" |
|
||
|
||
---
|
||
|
||
## 4. 数据模型
|
||
|
||
### 4.1 书单表 (fa_reading_book_list)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| title | varchar(100) | ✅ | 书单标题 |
|
||
| description | varchar(500) | ❌ | 书单描述 |
|
||
| cover_image | varchar(500) | ❌ | 书单封面图 URL |
|
||
| sort_order | int | ❌ | 排序权重(越小越靠前,默认 0) |
|
||
| status | varchar(20) | ✅ | 状态:active / inactive |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
### 4.2 书籍表 (fa_reading_book)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| book_list_id | bigint | ✅ | 所属书单 ID |
|
||
| title | varchar(200) | ✅ | 书名 |
|
||
| author | varchar(100) | ❌ | 作者 |
|
||
| cover_image | varchar(500) | ❌ | 书籍封面图 URL |
|
||
| description | text | ❌ | 内容简介 |
|
||
| isbn | varchar(20) | ❌ | ISBN 编号 |
|
||
| total_pages | int | ❌ | 总页数(用于 AI 生成计划) |
|
||
| total_plan_days | int | ❌ | 计划总天数 |
|
||
| sort_order | int | ❌ | 排序权重(默认 0) |
|
||
| status | varchar(20) | ✅ | 状态:active / inactive |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
**索引**:
|
||
- `idx_book_list_id` (book_list_id)
|
||
- `idx_status` (status)
|
||
|
||
### 4.3 阅读计划表 (fa_reading_plan)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| book_id | bigint | ✅ | 关联书籍 ID(唯一) |
|
||
| total_days | int | ✅ | 计划总天数 |
|
||
| total_estimated_minutes | int | ❌ | AI 预估总阅读时长(分钟,所有天数累加) |
|
||
| ai_generated | tinyint(1) | ✅ | 是否 AI 生成(1=是,0=否) |
|
||
| ai_model | varchar(50) | ❌ | 生成时使用的 AI 模型 |
|
||
| status | varchar(20) | ✅ | 状态:draft / published |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
**索引**:
|
||
- `uidx_book_id` (book_id) UNIQUE
|
||
|
||
### 4.4 每日导读表 (fa_reading_plan_day)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| plan_id | bigint | ✅ | 关联阅读计划 ID |
|
||
| day_number | int | ✅ | 第几天(从 1 开始) |
|
||
| title | varchar(200) | ✅ | 导读标题(如"第三章 农业革命") |
|
||
| guide_content | text | ✅ | 导读正文(AI 生成的阅读引导) |
|
||
| reading_range | varchar(100) | ❌ | 阅读范围(如"P45-P62"或"第三章") |
|
||
| estimated_minutes | int | ✅ | AI 预估阅读时长(分钟) |
|
||
| questions_json | json | ❌ | 理解问题数组 `[{"q":"问题内容","hint":"提示"}]` |
|
||
| sort_order | int | ❌ | 排序(默认等于 day_number) |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
**索引**:
|
||
- `uidx_plan_day` (plan_id, day_number) UNIQUE
|
||
- `idx_plan_id` (plan_id)
|
||
|
||
**questions_json 结构**:
|
||
```json
|
||
[
|
||
{
|
||
"q": "作者为什么认为农业革命是'史上最大的骗局'?",
|
||
"hint": "可以从人类生活质量、劳动强度等角度思考"
|
||
},
|
||
{
|
||
"q": "采集社会与农业社会相比各有什么优劣?",
|
||
"hint": ""
|
||
}
|
||
]
|
||
```
|
||
|
||
### 4.5 用户阅读进度表 (fa_reading_user_book)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| uid | bigint | ✅ | 用户 ID |
|
||
| book_id | bigint | ✅ | 书籍 ID |
|
||
| plan_id | bigint | ✅ | 阅读计划 ID |
|
||
| current_day | int | ✅ | 当前进度(已完成到第几天,默认 0) |
|
||
| total_days | int | ✅ | 计划总天数(冗余,方便查询) |
|
||
| status | varchar(20) | ✅ | 状态:reading / completed / dropped |
|
||
| started_at | timestamp | ✅ | 开始阅读时间 |
|
||
| completed_at | timestamp | ❌ | 读完时间 |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
**索引**:
|
||
- `uidx_uid_book` (uid, book_id) UNIQUE
|
||
- `idx_uid_status` (uid, status)
|
||
|
||
### 4.6 每日打卡表 (fa_reading_daily_checkin)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| uid | bigint | ✅ | 用户 ID |
|
||
| user_book_id | bigint | ✅ | 关联用户阅读进度 ID |
|
||
| plan_day_id | bigint | ✅ | 关联每日导读 ID |
|
||
| day_number | int | ✅ | 第几天 |
|
||
| date | date | ✅ | 打卡自然日 |
|
||
| estimated_minutes | int | ✅ | AI 预估阅读时长(冗余自 plan_day) |
|
||
| answers_json | json | ❌ | 问题回答 `[{"q":"问题","a":"回答"}]` |
|
||
| reflection | text | ❌ | 读书感悟 |
|
||
| status | varchar(20) | ✅ | 状态:checked_in / missed |
|
||
| checked_in_at | timestamp | ❌ | 打卡时间 |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
**索引**:
|
||
- `uidx_uid_date` (uid, date) UNIQUE
|
||
- `idx_uid_user_book` (uid, user_book_id)
|
||
- `idx_user_book_day` (user_book_id, day_number)
|
||
|
||
**answers_json 结构**:
|
||
```json
|
||
[
|
||
{
|
||
"q": "作者为什么认为农业革命是'史上最大的骗局'?",
|
||
"a": "因为农业让人类的劳动量大幅增加,饮食更加单一,但总人口的增长又让人们无法回到采集社会..."
|
||
}
|
||
]
|
||
```
|
||
|
||
### 4.7 分享记录表 (fa_reading_share)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | bigint | PK | 主键 |
|
||
| uid | bigint | ✅ | 用户 ID |
|
||
| share_type | varchar(20) | ✅ | 分享类型:daily / book_complete / streak_milestone |
|
||
| template_code | varchar(50) | ✅ | 海报模板编码 |
|
||
| data_json | json | ❌ | 海报数据快照 |
|
||
| image_url | varchar(500) | ❌ | 生成的海报图片 URL |
|
||
| created_at | timestamp | 自动 | 创建时间 |
|
||
| updated_at | timestamp | 自动 | 更新时间 |
|
||
| deleted_at | timestamp | 自动 | 软删除时间 |
|
||
|
||
**索引**:
|
||
- `idx_uid` (uid)
|
||
|
||
---
|
||
|
||
## 5. API 设计
|
||
|
||
### 5.1 路由前缀与鉴权
|
||
|
||
| 前缀 | 鉴权 | 用途 |
|
||
|------|------|------|
|
||
| `/api/reading` | 部分公开 | 小程序主接口 |
|
||
| `/api/admin/reading` | Admin JWT | 管理后台 |
|
||
|
||
### 5.2 书单与书籍(公开接口)
|
||
|
||
#### 获取书单列表
|
||
```
|
||
GET /api/reading/book-lists
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"title": "经典必读书单",
|
||
"description": "精选 10 本改变思维方式的经典作品",
|
||
"cover_image": "https://...",
|
||
"book_count": 10
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 获取书单下的书籍列表
|
||
```
|
||
GET /api/reading/book-lists/:id/books
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"book_list": { "id": 1, "title": "经典必读书单" },
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"title": "人类简史",
|
||
"author": "尤瓦尔·赫拉利",
|
||
"cover_image": "https://...",
|
||
"total_plan_days": 30,
|
||
"total_estimated_minutes": 750,
|
||
"description": "..."
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 获取书籍详情(含计划概览)
|
||
```
|
||
GET /api/reading/books/:id
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"book": {
|
||
"id": 1,
|
||
"title": "人类简史",
|
||
"author": "尤瓦尔·赫拉利",
|
||
"cover_image": "https://...",
|
||
"description": "...",
|
||
"total_pages": 440
|
||
},
|
||
"plan": {
|
||
"id": 1,
|
||
"total_days": 30,
|
||
"total_estimated_minutes": 750,
|
||
"status": "published",
|
||
"days_preview": [
|
||
{
|
||
"day_number": 1,
|
||
"title": "序章 - 人类的三大革命",
|
||
"reading_range": "P1-P15",
|
||
"estimated_minutes": 20
|
||
},
|
||
{
|
||
"day_number": 2,
|
||
"title": "第一章 - 人类:一种也没什么特别的动物",
|
||
"reading_range": "P16-P32",
|
||
"estimated_minutes": 25
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 用户阅读(需登录)
|
||
|
||
#### 开始阅读一本书
|
||
```
|
||
POST /api/reading/books/:id/start
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"user_book_id": 1,
|
||
"book_id": 1,
|
||
"book_title": "人类简史",
|
||
"current_day": 0,
|
||
"total_days": 30,
|
||
"status": "reading",
|
||
"started_at": "2026-03-21T10:00:00+08:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
**业务规则**:
|
||
- 同一本书不能重复开始(返回 409)
|
||
- 同时只能在读一本书(可配置,MVP 先限制 1 本)
|
||
|
||
#### 获取我的阅读列表
|
||
```
|
||
GET /api/reading/my/books
|
||
Query:
|
||
- status: string (reading/completed/dropped/all,默认 all)
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"user_book_id": 1,
|
||
"book_id": 1,
|
||
"book_title": "人类简史",
|
||
"book_cover": "https://...",
|
||
"author": "尤瓦尔·赫拉利",
|
||
"current_day": 5,
|
||
"total_days": 30,
|
||
"progress_percent": 17,
|
||
"status": "reading",
|
||
"started_at": "2026-03-15T10:00:00+08:00"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 获取某本书的阅读进度详情
|
||
```
|
||
GET /api/reading/my/books/:user_book_id
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"user_book": {
|
||
"user_book_id": 1,
|
||
"book_id": 1,
|
||
"book_title": "人类简史",
|
||
"author": "尤瓦尔·赫拉利",
|
||
"current_day": 5,
|
||
"total_days": 30,
|
||
"status": "reading"
|
||
},
|
||
"checkin_history": [
|
||
{
|
||
"day_number": 5,
|
||
"date": "2026-03-20",
|
||
"title": "第三章 农业革命",
|
||
"estimated_minutes": 25,
|
||
"has_reflection": true,
|
||
"checked_in_at": "2026-03-20T21:30:00+08:00"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.4 每日打卡(需登录)
|
||
|
||
#### 获取今日阅读任务
|
||
```
|
||
GET /api/reading/my/books/:user_book_id/today
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"daily_status": "pending", // pending / checked_in
|
||
"day_number": 6,
|
||
"total_days": 30,
|
||
"plan_day": {
|
||
"id": 6,
|
||
"title": "第三章 农业革命(续)",
|
||
"guide_content": "上一节我们了解了农业革命的起源...",
|
||
"reading_range": "P63-P80",
|
||
"estimated_minutes": 25,
|
||
"questions": [
|
||
{
|
||
"q": "农业社会带来了哪些社会结构的变化?",
|
||
"hint": "可以从私有制、阶级分化等角度思考"
|
||
},
|
||
{
|
||
"q": "作者认为小麦'驯化'了人类,你怎么理解?",
|
||
"hint": ""
|
||
}
|
||
]
|
||
},
|
||
"checkin": null // 已打卡时返回打卡详情
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 提交今日打卡
|
||
```
|
||
POST /api/reading/my/books/:user_book_id/checkin
|
||
Body:
|
||
{
|
||
"answers": [
|
||
{
|
||
"q": "农业社会带来了哪些社会结构的变化?",
|
||
"a": "农业带来了定居生活,随之产生了私有制和社会阶级分化..."
|
||
},
|
||
{
|
||
"q": "作者认为小麦'驯化'了人类,你怎么理解?",
|
||
"a": "从小麦的角度看,它成功地让人类为它服务..."
|
||
}
|
||
],
|
||
"reflection": "今天读到农业革命的部分很有启发,原来我们以为的进步..."
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"checkin_id": 10,
|
||
"day_number": 6,
|
||
"date": "2026-03-21",
|
||
"estimated_minutes": 25,
|
||
"status": "checked_in",
|
||
"checked_in_at": "2026-03-21T22:00:00+08:00",
|
||
"summary": {
|
||
"current_streak_days": 6,
|
||
"max_streak_days": 6,
|
||
"total_reading_minutes": 145,
|
||
"total_books_completed": 0,
|
||
"book_progress_percent": 20,
|
||
"is_book_completed": false
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**业务规则**:
|
||
- 同一天同一本书只能打卡一次(返回 409)
|
||
- `answers` 数组至少包含 1 条非空回答
|
||
- `reflection` 选填
|
||
- 打卡成功后 `user_book.current_day` 自增 1
|
||
- 若 `current_day == total_days`,自动将 `user_book.status` 更新为 `completed`
|
||
- 当天未打卡自然过期后标记为 `missed`(可通过定时任务或下次请求时回填)
|
||
|
||
#### 获取打卡记录列表
|
||
```
|
||
GET /api/reading/my/books/:user_book_id/checkins
|
||
Query:
|
||
- page: int (默认 1)
|
||
- page_size: int (默认 20)
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 10,
|
||
"day_number": 6,
|
||
"date": "2026-03-21",
|
||
"title": "第三章 农业革命(续)",
|
||
"estimated_minutes": 25,
|
||
"answers_count": 2,
|
||
"has_reflection": true,
|
||
"checked_in_at": "2026-03-21T22:00:00+08:00"
|
||
}
|
||
],
|
||
"total": 6,
|
||
"page": 1,
|
||
"page_size": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.5 读书感悟(需登录)
|
||
|
||
#### 获取某本书的感悟列表
|
||
```
|
||
GET /api/reading/my/books/:user_book_id/reflections
|
||
Query:
|
||
- page: int (默认 1)
|
||
- page_size: int (默认 20)
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"day_number": 6,
|
||
"date": "2026-03-21",
|
||
"title": "第三章 农业革命(续)",
|
||
"reflection": "今天读到农业革命的部分很有启发..."
|
||
}
|
||
],
|
||
"total": 3,
|
||
"page": 1,
|
||
"page_size": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.6 阅读统计(需登录)
|
||
|
||
#### 获取统计概览
|
||
```
|
||
GET /api/reading/my/stats
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"current_streak_days": 6,
|
||
"max_streak_days": 6,
|
||
"total_reading_minutes": 145,
|
||
"total_books_completed": 0,
|
||
"total_checkin_days": 6,
|
||
"current_book": {
|
||
"book_title": "人类简史",
|
||
"progress_percent": 20,
|
||
"current_day": 6,
|
||
"total_days": 30
|
||
},
|
||
"recent_checkins": [
|
||
{ "date": "2026-03-21", "status": "checked_in", "estimated_minutes": 25 },
|
||
{ "date": "2026-03-20", "status": "checked_in", "estimated_minutes": 20 },
|
||
{ "date": "2026-03-19", "status": "checked_in", "estimated_minutes": 30 }
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.7 分享海报(需登录)
|
||
|
||
#### 获取海报预览数据
|
||
```
|
||
GET /api/reading/poster/data
|
||
Query:
|
||
- template_code: string (默认 "reading_1")
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"nickname": "读书人",
|
||
"streak_days": 6,
|
||
"total_books_completed": 2,
|
||
"total_reading_minutes": 1450,
|
||
"current_book_title": "人类简史",
|
||
"current_book_progress": "6/30",
|
||
"template_code": "reading_1",
|
||
"share_title": "我已连续阅读打卡 6 天"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 生成分享海报
|
||
```
|
||
POST /api/reading/poster/generate
|
||
Body:
|
||
{
|
||
"template_code": "reading_1",
|
||
"show_fields": ["streak_days", "total_books_completed", "total_reading_minutes"]
|
||
}
|
||
|
||
Response:
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"template_code": "reading_1",
|
||
"image_url": "https://static.nepiedg.top/reading/posters/...",
|
||
"share_title": "我已连续阅读打卡 6 天"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.8 管理后台 API
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| **书单管理** | | |
|
||
| GET | `/api/admin/reading/book-lists` | 书单列表(支持分页) |
|
||
| POST | `/api/admin/reading/book-lists` | 创建书单 |
|
||
| PUT | `/api/admin/reading/book-lists/:id` | 更新书单 |
|
||
| DELETE | `/api/admin/reading/book-lists/:id` | 删除书单 |
|
||
| **书籍管理** | | |
|
||
| GET | `/api/admin/reading/books` | 书籍列表(支持按书单筛选) |
|
||
| POST | `/api/admin/reading/books` | 创建书籍 |
|
||
| PUT | `/api/admin/reading/books/:id` | 更新书籍 |
|
||
| DELETE | `/api/admin/reading/books/:id` | 删除书籍 |
|
||
| **阅读计划管理** | | |
|
||
| GET | `/api/admin/reading/plans/:book_id` | 获取书籍的阅读计划 |
|
||
| POST | `/api/admin/reading/plans/:book_id/generate` | AI 生成阅读计划 |
|
||
| PUT | `/api/admin/reading/plans/:plan_id` | 更新阅读计划状态 |
|
||
| **导读内容管理** | | |
|
||
| GET | `/api/admin/reading/plan-days?plan_id=X` | 获取某计划的所有导读 |
|
||
| PUT | `/api/admin/reading/plan-days/:id` | 编辑单日导读内容 |
|
||
|
||
---
|
||
|
||
## 6. 核心业务流程
|
||
|
||
### 6.1 用户打卡流程
|
||
|
||
```
|
||
用户进入"今日阅读"首页
|
||
│
|
||
├── 未选书 → 展示空状态 → 引导去"书单"页选书
|
||
│ │
|
||
│ ↓
|
||
│ 点击书籍 → 书籍详情页
|
||
│ │
|
||
│ ↓
|
||
│ 点击"开始阅读"
|
||
│ │
|
||
│ POST /books/:id/start
|
||
│ │
|
||
│ ↓
|
||
│ 返回首页(展示在读书籍)
|
||
│
|
||
└── 有在读书 → GET /my/books/:id/today
|
||
│
|
||
├── 今日已打卡 (daily_status = checked_in)
|
||
│ → 展示"今日已完成 ✅"
|
||
│ → 展示打卡记录(时长、回答、感悟)
|
||
│
|
||
└── 今日未打卡 (daily_status = pending)
|
||
→ 展示今日导读内容
|
||
→ 展示 AI 预估阅读时长(如"预估 25 分钟")
|
||
→ 用户阅读完成后:
|
||
│
|
||
├── 1) 回答理解问题(至少 1 题)
|
||
├── 2) 写读书感悟(选填)
|
||
└── 3) 点击"完成打卡"
|
||
│
|
||
POST /my/books/:id/checkin
|
||
│
|
||
├── 普通天 → 打卡成功
|
||
│ → current_day + 1
|
||
│ → 展示鼓励动画 + 连续天数
|
||
│
|
||
└── 最后一天 (current_day == total_days)
|
||
→ user_book.status = completed
|
||
→ 弹出"🎉 恭喜读完全书!"
|
||
→ 引导生成分享宣传图
|
||
```
|
||
|
||
### 6.2 AI 阅读计划生成流程(后台)
|
||
|
||
```
|
||
管理员在后台创建书籍
|
||
│
|
||
↓
|
||
点击"AI 生成阅读计划"
|
||
│
|
||
↓
|
||
POST /admin/reading/plans/:book_id/generate
|
||
│
|
||
├── 输入给 AI:
|
||
│ - 书名、作者、简介、总页数
|
||
│ - 期望计划天数(如 30 天)
|
||
│
|
||
├── AI 返回(结构化 JSON):
|
||
│ - 每日导读标题 (title)
|
||
│ - 导读正文 (guide_content)
|
||
│ - 阅读范围 (reading_range)
|
||
│ - 预估阅读时长 (estimated_minutes)
|
||
│ - 理解问题 (questions)
|
||
│
|
||
├── 写入 fa_reading_plan 表
|
||
│ - ai_generated = true
|
||
│ - status = draft
|
||
│
|
||
└── 写入 fa_reading_plan_day 表(逐日)
|
||
│
|
||
↓
|
||
管理员逐日审核/编辑导读内容、时长、问题
|
||
│
|
||
↓
|
||
将计划状态改为 published → 前端可见
|
||
```
|
||
|
||
**AI Prompt 参考**:
|
||
```
|
||
你是一位资深阅读导师,请为以下书籍制定一个 {total_days} 天的阅读计划:
|
||
|
||
书名:{title}
|
||
作者:{author}
|
||
简介:{description}
|
||
总页数:{total_pages}
|
||
|
||
要求:
|
||
1. 将全书合理拆分为 {total_days} 天的阅读任务
|
||
2. 每天包含:导读标题、导读正文(200-300字,引导读者关注重点)、阅读范围(页码)、预估阅读时长(分钟)、2-3 道理解问题
|
||
3. 预估阅读时长应考虑该部分的难度和信息密度
|
||
4. 理解问题应引导深度思考,而非简单的信息检索
|
||
|
||
请以 JSON 数组格式返回,每个元素包含:
|
||
{
|
||
"day_number": 1,
|
||
"title": "导读标题",
|
||
"guide_content": "导读正文",
|
||
"reading_range": "P1-P15",
|
||
"estimated_minutes": 20,
|
||
"questions": [
|
||
{"q": "问题内容", "hint": "思考提示"}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 6.3 分享宣传图流程
|
||
|
||
```
|
||
用户点击"分享成就"
|
||
│
|
||
↓
|
||
GET /reading/poster/data → 获取海报数据
|
||
│
|
||
↓
|
||
选择海报模板(多种风格)
|
||
│
|
||
↓
|
||
POST /reading/poster/generate → 生成海报图片
|
||
│
|
||
↓
|
||
预览海报
|
||
│
|
||
├── "保存到相册" → wx.saveImageToPhotosAlbum
|
||
└── "分享给好友" → open-type="share"
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 后端模块结构
|
||
|
||
遵循现有 `wx_service` 的 Handler → Service → Model 分层架构:
|
||
|
||
```
|
||
wx_service/internal/reading/
|
||
├── handler/
|
||
│ └── handler.go # HTTP 接口处理(入参校验、调用 Service、返回 JSON)
|
||
├── model/
|
||
│ ├── book_list.go # 书单模型 (fa_reading_book_list)
|
||
│ ├── book.go # 书籍模型 (fa_reading_book)
|
||
│ ├── reading_plan.go # 阅读计划模型 (fa_reading_plan)
|
||
│ ├── plan_day.go # 每日导读模型 (fa_reading_plan_day)
|
||
│ ├── user_book.go # 用户阅读进度模型 (fa_reading_user_book)
|
||
│ ├── daily_checkin.go # 每日打卡模型 (fa_reading_daily_checkin)
|
||
│ └── share.go # 分享记录模型 (fa_reading_share)
|
||
└── service/
|
||
└── service.go # 业务逻辑(打卡、统计、计划生成、海报)
|
||
```
|
||
|
||
### 7.1 接入 main.go 的步骤
|
||
|
||
```go
|
||
// 1. 在 AutoMigrate 中注册所有 Model
|
||
database.AutoMigrate(
|
||
// ... 现有模型 ...
|
||
&readingmodel.BookList{},
|
||
&readingmodel.Book{},
|
||
&readingmodel.ReadingPlan{},
|
||
&readingmodel.PlanDay{},
|
||
&readingmodel.UserBook{},
|
||
&readingmodel.DailyCheckin{},
|
||
&readingmodel.Share{},
|
||
)
|
||
|
||
// 2. 创建 Service
|
||
readingService := readingservice.NewService(database.DB, config.AppConfig.AI)
|
||
|
||
// 3. 创建 Handler
|
||
readingHandler := readinghandler.NewHandler(readingService)
|
||
|
||
// 4. 传入 routes.Register 并注册路由
|
||
```
|
||
|
||
### 7.2 路由注册
|
||
|
||
```go
|
||
// routes.go 中新增
|
||
readingAPI := router.Group("/api/reading")
|
||
{
|
||
// 公开接口
|
||
readingAPI.GET("/book-lists", readingHandler.ListBookLists)
|
||
readingAPI.GET("/book-lists/:id/books", readingHandler.ListBooksByList)
|
||
readingAPI.GET("/books/:id", readingHandler.GetBookDetail)
|
||
|
||
// 需登录接口
|
||
readingProtected := readingAPI.Group("")
|
||
readingProtected.Use(middleware.AuthMiddleware(db, sessionCache))
|
||
readingProtected.Use(middleware.RequireUserMiddleware())
|
||
{
|
||
readingProtected.POST("/books/:id/start", readingHandler.StartReading)
|
||
readingProtected.GET("/my/books", readingHandler.ListMyBooks)
|
||
readingProtected.GET("/my/books/:id", readingHandler.GetMyBookDetail)
|
||
readingProtected.GET("/my/books/:id/today", readingHandler.GetTodayTask)
|
||
readingProtected.POST("/my/books/:id/checkin", readingHandler.Checkin)
|
||
readingProtected.GET("/my/books/:id/checkins", readingHandler.ListCheckins)
|
||
readingProtected.GET("/my/books/:id/reflections", readingHandler.ListReflections)
|
||
readingProtected.GET("/my/stats", readingHandler.Stats)
|
||
readingProtected.GET("/poster/data", readingHandler.PosterData)
|
||
readingProtected.POST("/poster/generate", readingHandler.GeneratePoster)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 前端项目结构
|
||
|
||
```
|
||
apps/reading-checkin/
|
||
├── src/
|
||
│ ├── api/
|
||
│ │ ├── auth.js # 微信登录(复用现有模式)
|
||
│ │ ├── request.js # 请求封装(Bearer Token + 401 重试)
|
||
│ │ ├── book.js # 书单/书籍相关 API
|
||
│ │ ├── checkin.js # 打卡相关 API
|
||
│ │ └── poster.js # 海报相关 API
|
||
│ ├── components/
|
||
│ │ ├── book-card/ # 书籍卡片组件
|
||
│ │ ├── checkin-calendar/ # 打卡日历组件
|
||
│ │ ├── progress-bar/ # 阅读进度条
|
||
│ │ └── question-card/ # 问题回答卡片
|
||
│ ├── pages/
|
||
│ │ ├── home/index.vue # 今日阅读(首页)
|
||
│ │ ├── book-lists/index.vue # 书单列表
|
||
│ │ ├── book-detail/index.vue# 书籍详情
|
||
│ │ ├── reading/index.vue # 阅读打卡页
|
||
│ │ ├── stats/index.vue # 统计
|
||
│ │ ├── profile/index.vue # 我的
|
||
│ │ └── poster/index.vue # 分享海报
|
||
│ ├── stores/
|
||
│ │ ├── index.js # Pinia store 入口
|
||
│ │ └── user.js # 用户状态
|
||
│ ├── utils/
|
||
│ │ ├── format.js # 时长、日期格式化
|
||
│ │ └── storage.js # reading_checkin_ 前缀存储
|
||
│ ├── config/
|
||
│ │ └── index.js # mini_program_id, baseUrl 配置
|
||
│ ├── App.vue # 应用入口(onLaunch 登录)
|
||
│ ├── main.js
|
||
│ ├── manifest.json
|
||
│ ├── pages.json # 页面路由 + TabBar 配置
|
||
│ └── uni.scss # 全局样式
|
||
├── package.json
|
||
└── vite.config.js
|
||
```
|
||
|
||
### 8.1 关键配置
|
||
|
||
**config/index.js**:
|
||
```javascript
|
||
const config = {
|
||
mini_program_id: '<reading_checkin_mp_id>',
|
||
baseUrl: {
|
||
dev: 'http://localhost:8080/api',
|
||
prod: 'https://wx.nepiedg.top/api'
|
||
}
|
||
}
|
||
```
|
||
|
||
**utils/storage.js** 存储前缀: `reading_checkin_`
|
||
|
||
---
|
||
|
||
## 9. 实施计划
|
||
|
||
| 阶段 | 内容 | 优先级 |
|
||
|------|------|--------|
|
||
| **P0: 后端基础** | Model 定义 + AutoMigrate + Service 骨架 + Handler + Routes 接入 | 高 |
|
||
| **P0: 前端脚手架** | 基于 quit-checkin 创建项目骨架、登录、请求封装 | 高 |
|
||
| **P1: 书单与书籍** | 后台 CRUD + 前端展示 | 高 |
|
||
| **P1: AI 阅读计划** | 后台 AI 生成 + 导读编辑 | 高 |
|
||
| **P1: 打卡核心流程** | 今日任务获取 + 回答问题 + 提交打卡 + 进度更新 | 高 |
|
||
| **P2: 统计页面** | 连续天数、日历、趋势图 | 中 |
|
||
| **P2: 分享海报** | 海报数据 + 模板 + 生成 | 中 |
|
||
| **P3: 优化体验** | 打卡动画、空状态引导、错误处理 | 低 |
|
||
| **P3: 联调测试** | 前后端联调 + 微信真机测试 | 低 |
|
||
|
||
---
|
||
|
||
## 10. 与现有系统的关系
|
||
|
||
| 复用项 | 说明 |
|
||
|--------|------|
|
||
| 用户表 (`users`) | 共用,通过 `mini_program_id` 区分小程序 |
|
||
| 微信登录 (`AuthService`) | 完全复用 |
|
||
| 鉴权中间件 (`AuthMiddleware`) | 完全复用 |
|
||
| 七牛上传 (`QiniuService`) | 复用(书籍封面、海报图片上传) |
|
||
| Redis 缓存 (`SessionUserCache`) | 复用 |
|
||
| 管理后台框架 (`admin`) | 复用 JWT 鉴权和管理员体系 |
|
||
| AI 配置 (`config.AI`) | 复用 AI 调用配置(参照 smoke 模块的 AI 集成) |
|