34 KiB
34 KiB
阅读打卡小程序 - 产品需求文档 (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) UNIQUEidx_plan_id(plan_id)
questions_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) UNIQUEidx_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) UNIQUEidx_uid_user_book(uid, user_book_id)idx_user_book_day(user_book_id, day_number)
answers_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 的步骤
// 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 路由注册
// 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:
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 集成) |