# 阅读打卡小程序 - 产品需求文档 (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: '', 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 集成) |