|
|
@@ -0,0 +1,198 @@
|
|
|
+# 文章详情页功能状态与补充设计
|
|
|
+
|
|
|
+## 当前状态
|
|
|
+
|
|
|
+| 功能 | 状态 | 说明 |
|
|
|
+|------|------|------|
|
|
|
+| 阅读计时 | ✅ 已完成 | 每秒计时,每30秒上报后端 |
|
|
|
+| 分享 | ✅ 已完成 | 微信分享 + 海报生成 |
|
|
|
+| 自动完成阅读 | ❌ 未实现 | 达到阅读时长后自动触发 |
|
|
|
+| 文章评论 | ❌ 未实现 | 需新建评论系统 |
|
|
|
+| 评论审核 | ❌ 未实现 | 管理员/发布人审核 |
|
|
|
+| AI答题 | ❌ 未实现 | 浠宝/福宝提问 |
|
|
|
+| 评论能量 | ❌ 未实现 | 评论后奖励能量 |
|
|
|
+| 答题能量 | ❌ 未实现 | 答完题奖励能量 |
|
|
|
+
|
|
|
+## 补充设计方案
|
|
|
+
|
|
|
+### 一、自动完成阅读
|
|
|
+
|
|
|
+**触发条件:** `readingSeconds >= article.readTime * 60`
|
|
|
+
|
|
|
+**前端逻辑(article-detail.vue):**
|
|
|
+```javascript
|
|
|
+reportReadingTime() {
|
|
|
+ // 现有上报逻辑...
|
|
|
+
|
|
|
+ // 新增:检查是否达到完成条件
|
|
|
+ if (!this.readingCompleted && this.readingSeconds >= (this.article.readTime || 3) * 60) {
|
|
|
+ this.completeReading()
|
|
|
+ }
|
|
|
+},
|
|
|
+async completeReading() {
|
|
|
+ this.readingCompleted = true
|
|
|
+ // 隐藏计时器显示
|
|
|
+ // 调用后端记录完成阅读
|
|
|
+ await completeArticleRead({ articleId, childId, durationSeconds: this.readingSeconds })
|
|
|
+ // 弹出浠宝答题
|
|
|
+ this.showQuizDialog()
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**前端显示变化:**
|
|
|
+- 达到时长后,右上角的 `已读 X分X秒` 标签隐藏
|
|
|
+- 后台继续计时但不显示
|
|
|
+
|
|
|
+**后端新增接口:**
|
|
|
+```java
|
|
|
+POST /api/articles/complete-read
|
|
|
+body: { articleId, childId, durationSeconds }
|
|
|
+→ 创建 ArticleReadingRecord(如果已达阅读时长)
|
|
|
+→ 发放积分 + 能量
|
|
|
+```
|
|
|
+
|
|
|
+### 二、文章评论系统
|
|
|
+
|
|
|
+#### 数据库表
|
|
|
+
|
|
|
+```sql
|
|
|
+CREATE TABLE article_comments (
|
|
|
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
|
+ article_id BIGINT NOT NULL COMMENT '文章ID',
|
|
|
+ parent_id BIGINT COMMENT '父评论ID(回复时填充)',
|
|
|
+ reply_to_id BIGINT COMMENT '引用的评论ID',
|
|
|
+ user_id BIGINT NOT NULL COMMENT '评论用户ID',
|
|
|
+ content TEXT NOT NULL COMMENT '评论内容',
|
|
|
+ status VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/approved/rejected',
|
|
|
+ audit_by BIGINT COMMENT '审核人ID',
|
|
|
+ audit_at DATETIME COMMENT '审核时间',
|
|
|
+ earned_energy INT DEFAULT 0 COMMENT '获得的能量',
|
|
|
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ INDEX idx_article (article_id),
|
|
|
+ INDEX idx_article_status (article_id, status),
|
|
|
+ INDEX idx_parent (parent_id)
|
|
|
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章评论';
|
|
|
+```
|
|
|
+
|
|
|
+#### 后端接口
|
|
|
+
|
|
|
+| 接口 | 说明 | 角色 |
|
|
|
+|------|------|------|
|
|
|
+| `POST /api/articles/comments/list` | 获取已审核评论列表 | 所有人 |
|
|
|
+| `POST /api/articles/comments/create` | 提交评论(状态=pending) | 登录用户 |
|
|
|
+| `POST /api/articles/comments/audit` | 审核评论(approved/rejected) | 管理员/文章发布人 |
|
|
|
+| `POST /api/articles/comments/my` | 查看我的评论 | 登录用户 |
|
|
|
+
|
|
|
+#### 审核流程
|
|
|
+
|
|
|
+```
|
|
|
+用户提交评论 → status=pending
|
|
|
+ ├─ 文章发布人 → 可在文章详情页审核
|
|
|
+ ├─ 文章管理员 → 可在管理后台审核
|
|
|
+ └─ 超级管理员 → 可在管理后台审核
|
|
|
+ ↓
|
|
|
+ 审核通过 → status=approved → 前端显示
|
|
|
+ 审核驳回 → status=rejected → 仅作者可见(显示"审核未通过")
|
|
|
+```
|
|
|
+
|
|
|
+#### 评论的回复结构
|
|
|
+
|
|
|
+```
|
|
|
+评论A(用户1)
|
|
|
+ ├─ 回复B(用户2)引用"评论A"内容
|
|
|
+ │ └─ 回复C(用户1)引用"回复B"内容(同一层,通过reply_to_id引用)
|
|
|
+ └─ 回复D(用户3)引用"评论A"内容
|
|
|
+```
|
|
|
+
|
|
|
+前端展示:
|
|
|
+```
|
|
|
+[用户1] 这篇文章写得真好!
|
|
|
+ ├ [用户2] @用户1 同意!→ 引用原文
|
|
|
+ └ [用户3] @用户1 学到了很多
|
|
|
+```
|
|
|
+
|
|
|
+### 三、AI答题功能
|
|
|
+
|
|
|
+#### 触发时机
|
|
|
+
|
|
|
+阅读完成时 → 自动弹出浠宝/福宝对话框
|
|
|
+
|
|
|
+#### 交互流程
|
|
|
+
|
|
|
+```
|
|
|
+浠宝弹出:"👋 阅读完成!考考你三个问题~"
|
|
|
+ ↓
|
|
|
+问题1: "文章中提到...?"(从文章内容提取)
|
|
|
+ → 用户选择/输入答案
|
|
|
+ ↓
|
|
|
+问题2: "..."
|
|
|
+ → 用户选择/输入答案
|
|
|
+ ↓
|
|
|
+问题3: "..."
|
|
|
+ → 用户选择/输入答案
|
|
|
+ ↓
|
|
|
+"🎉 全部答对!获得 15 能量!"
|
|
|
+ ↓
|
|
|
+关闭对话框,回到文章底部
|
|
|
+```
|
|
|
+
|
|
|
+#### 后端实现
|
|
|
+
|
|
|
+使用 Dify Workflow API 生成题目:
|
|
|
+```java
|
|
|
+POST /api/articles/quiz/generate
|
|
|
+body: { articleId, childId }
|
|
|
+→ 调用 Dify Workflow,传入文章内容
|
|
|
+→ 返回 3 个问题(含选项和正确答案)
|
|
|
+
|
|
|
+POST /api/articles/quiz/submit
|
|
|
+body: { articleId, childId, answers: [{questionId, answer}] }
|
|
|
+→ 比对答案
|
|
|
+→ 答对 X 题 → 发放 X*5 能量
|
|
|
+```
|
|
|
+
|
|
|
+#### 前端实现
|
|
|
+
|
|
|
+```vue
|
|
|
+<!-- 浠宝答题弹窗 -->
|
|
|
+<view class="quiz-dialog" v-if="showQuiz">
|
|
|
+ <view class="quiz-mascot">
|
|
|
+ <image :src="mascotAvatar" />
|
|
|
+ <text>{{ mascotName }}: {{ mascotMessage }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="quiz-question" v-for="(q, i) in quizQuestions" :key="i">
|
|
|
+ <text class="q-title">问题{{ i+1 }}: {{ q.question }}</text>
|
|
|
+ <view class="q-options">
|
|
|
+ <text v-for="opt in q.options" class="q-option"
|
|
|
+ :class="{ selected: selectedAnswers[i] === opt }"
|
|
|
+ @click="selectedAnswers[i] = opt">{{ opt }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <button @click="submitQuiz">提交答案</button>
|
|
|
+</view>
|
|
|
+```
|
|
|
+
|
|
|
+### 四、能量奖励汇总
|
|
|
+
|
|
|
+| 行为 | 能量值 | 说明 |
|
|
|
+|------|--------|------|
|
|
|
+| 完成阅读 | 5 | 达到阅读时长自动发放 |
|
|
|
+| 发表评论 | 3 | 评论提交后发放 |
|
|
|
+| 评论审核通过 | 2 | 额外奖励 |
|
|
|
+| 答题全对 | 15 | 3题全对 |
|
|
|
+| 答题对1题 | 5 | 答对1题 |
|
|
|
+| 答题对2题 | 10 | 答对2题 |
|
|
|
+
|
|
|
+### 五、实施路线图
|
|
|
+
|
|
|
+| 步骤 | 内容 | 涉及文件 | 估算工时 |
|
|
|
+|------|------|---------|---------|
|
|
|
+| 1 | 后端:ArticleComment 实体 + Mapper | 2文件 | 1h |
|
|
|
+| 2 | 后端:ArticleCommentController + Service | 2文件 | 2h |
|
|
|
+| 3 | 后端:article_comments 表迁移 + schema.sql | 2文件 | 0.5h |
|
|
|
+| 4 | 后端:完成阅读/答题/Dify接口 | 3文件 | 2h |
|
|
|
+| 5 | 前端:article-detail 评论区域 | 1文件 | 2h |
|
|
|
+| 6 | 前端:浠宝答题弹窗 | 1文件 | 2h |
|
|
|
+| 7 | 后端:管理后台评论审核接口 | 1文件 | 1h |
|
|
|
+| 8 | 前端:管理后台评论审核页面 | 1文件 | 1.5h |
|
|
|
+| | **合计** | **13文件** | **~12h** |
|