Files
wx_service/docs/reading/PRD.md
T

1090 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 阅读打卡小程序 - 产品需求文档 (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 集成) |